[python] имплементация слайсов для туплов

psihodog

Кто-нть знает, если я беру слайс у тупла, физически данные копируются или нет?
В основном интересует оригинальный интерпретатор CPython.

klyv

>>> t = tuple(range(100000
>>> p = []
>>> for i in range(50000):
p.append(t[i:-i])
Скушало очень многа памяти. Видать, копируется :)

bleyman

Ты имел в виду не "p+=t[i:-i]", а "p.append(t[i:-i])". Но да, копируются однако.

psihodog

ок. :( спасибо! :)

bleyman

Ты всегда можешь сделать свою обёртку!
Подпатчить сам класс tuple наверное нельзя, он небось на С целиком написан. А вот обёрточку можно сделать, и она даже наверное будет достаточно быстро работать, в присутствие Psyco.

pilot

Скушало очень многа памяти. Видать, копируется
Неправильный тест.
как ты узнал что копировались данные, а не ссылки на них (указатели)?
Upd: у тебя данные (int) и указатели примерно одного размера.

pilot

Подпатчить сам класс tuple наверное нельзя, он небось на С целиком написан
Это верно для старого питона, версии типа 2.2
>>> class tup(tuple):
... def __init__(self, *args):
... print 'foo'
... return tuple.__init__(self, *args)
...
>>> tup([1,2])
foo
(1, 2)

bleyman

Прочитай внимательно
Если бы получался объект "слайс", дающий прозрачный доступ к underlying immutable collection, то у него таких объектов получилось бы всего 50к штук, константного размера. А так у него получается 50к слайсов в среднем на 50к элементов каждый.

bleyman

я имел в виду подпатчить сам класс, но оно не даёт:
tuple.__getitem__ = mySlicer
TypeError: can't set attributes of built-in/extension type 'tuple'

klyv

Ты всегда можешь сделать свою обёртку!
Подпатчить сам класс tuple наверное нельзя, он небось на С целиком написан. А вот обёрточку можно сделать, и она даже наверное будет достаточно быстро работать, в присутствие Psyco.
Psyco необязательно - можно просто написать на С класс ;)

pilot

Прочитал вопрос внимательно, и еще раз говорю:
Есть tuple из 5 объектов, каждый объект размером 1мб.
Делаешь slice — первые два объекта tuple.
Объектов так и осталось 5 штук. Просто появилось 2 разных списка объектов — один это tuple из 5 указателей, второй это tuple из 2-х указателей на те же самые объекты.
Меняем 1 объект, он меняется в 2-х списках.
В коде:

fvv$ cat test.py
class Foo:
def __init__(self, i):
self.i = i
def __repr__(self):
return str(self.i)

a = tuple([Foo(x) for x in range(5)])
b = a[:2]
print a, b
a[0].i = 10
print a, b


fvv$ python test.py
(0, 1, 2, 3, 4) (0, 1)
(10, 1, 2, 3, 4) (10, 1)

Речь шла не об этом?
"Копируются ли объекты физически?" — "нет".

Dmitriy82

Элементы tuple (в твоём случае ссылки на объекты) копируются физически. А этого можно было бы избежать, представляя слайс как ссылку на исходный набор + диапазон индексов, благо immutable.

klyv

вопрос был, копируется ли содержимое slice. в твоём примере это - ссылки, в моём это - числа.
да, содержимое копируется. будь то ссылки или числа.

bleyman

Да речь шла не об этом.
А о том, что если ты берёшь первый миллион элементов из тупла на два миллиона элементов, создаётся новый тупл на миллион элементов. Хотя достаточно было бы вернуть специальный объект слайс, который во всём ведёт себя как тупл, но вместо элементов хранит ссылку на родительский тупл и параметры слайса.

VitMix

Хотя достаточно было бы вернуть специальный объект слайс, который во всём ведёт себя как тупл, но вместо элементов хранит ссылку на родительский тупл и параметры слайса.
Это хорошая идея, которая во многих (возможно в большинстве) случаев позволит сэкономить память. Однако, в некоорых случаях она, наоборот, расход памяти повысит (см пример в конце). Таким образом, у нас есть два подхода: "обёртка" и "копия". Каждый из подходов имеет достоинства и недостатки. "Обёртку" можно эффективно реализовать на питоне, а вот "копию" на питоне эффективно реализовать нельзя. Подозреваю, что именно поэтому авторы питона реализовали на C именно "копию", считая, что те, кому надо "обёртку", сами легко её напишут.
А теперь пример, когда "обёртка" хуже "копии":
цикл 1000 раз:
t = <получить тупл на миллион элементов>
s = <вырезать из t интересующий нас слайс размером 1000 элементов>
<сохранить s для последующего использования>
конец цикла
при использовании "обёрток" после выполнения цикла в памяти будет в общей сложности миллиард элементов (так как внутри каждого слайса размером 1000 элементов будет жить тупл размером миллион элементов а при использовании "копий" в памяти будет всего миллион элементов.

klyv

При использованиии обёрток в памяти будет 1000000 элеиентов и 1000 обёрток. Без них - 2000000 элементов... В чём проблемъ?

VitMix

Проблема в том, что тупл на миллион элементов на каждой итерации новый и миллон элементов в нём тоже свой.

klyv

ах, вон оно что...
тогда можно ещё аккуратнее реализовать обёртки ;)

psihodog

да, всё правильно...
только пример, когда копия лучше обёртки можно ещё проще придумать: скажем, если тебе дали тупл на миллион объектов, а тебе оттуда нужнен подтупл из 10 элементов, то в случае копии, после того как 10 элементов скопировал, тот миллион уже не нужен и его можно почистить, а в случае обёртки, эти десять элементов будут тянуть за собой изначальный миллион.
другой вопрос, что обе ситуации, на мой взгляд встречаются достаточно часто, так что можно было бы и поддержку обёртки встроить в язык (в смысле, в стдлиб).
и я бы, например, сделал так что, слайс возращал бы обёртку, а для явного копирования, можно было бы эту обёртку передать в конструктор туплу:
>>> t = range(1000000) # type(t) == 'tuple'
>>> s = t[123456:234567] # type(s) == 'subtuple', хранит ссылку на t, чтобы того не убили
>>> t2 = tuple(s) # здесь всё физически копируется как и из любого другого итерабла

bleyman

Вообще правильней было бы встроить алгоритм минимизации памяти прямо в реализацию языка и не отвлекать человеков от более важных мыслей.
Там же довольно просто всё. Особенно если не ставить целью нахождение строгого минимума.
Оставить комментарий
Имя или ник:
Комментарий: