У меня есть следующая проблема.
Учитывая список целых чисел
L
, мне нужно сгенерировать все подписиL[k:]
for k in [0, len(L) - 1]
, без генерации копий.
Как это сделать в Python? С каким-то буферным объектом?
У меня есть следующая проблема.
Учитывая список целых чисел
L
, мне нужно сгенерировать все подписиL[k:]
for k in [0, len(L) - 1]
, без генерации копий.
Как это сделать в Python? С каким-то буферным объектом?
Списки разреза не генерируют копии объектов в списке; он просто копирует ссылки на них. Это ответ на заданный вопрос.
Сначала давайте протестируем основное утверждение. Мы можем показать, что даже в случае неизменяемых объектов, таких как целые числа, копируется только ссылка. Вот три разных целочисленных объекта, каждый с одинаковым значением:
>>> a = [1000 + 1, 1000 + 1, 1000 + 1]
Они имеют одинаковое значение, но вы можете видеть, что они представляют собой три разных объекта, поскольку они имеют разные id
s:
>>> map(id, a)
[140502922988976, 140502922988952, 140502922988928]
Когда вы нарезаете их, ссылки остаются неизменными. Нет новых объектов:
>>> b = a[1:3]
>>> map(id, b)
[140502922988952, 140502922988928]
Использование разных объектов с тем же значением показывает, что процесс копирования не беспокоит interning - он просто копирует ссылки непосредственно.
Тестирование с изменяемыми значениями дает тот же результат:
>>> a = [{0: 'zero', 1: 'one'}, ['foo', 'bar']]
>>> map(id, a)
[4380777000, 4380712040]
>>> map(id, a[1:]
... )
[4380712040]
Конечно, сами копии копируются. Каждый из них стоит 8 байтов на 64-битной машине. И каждый список имеет свои собственные издержки памяти 72 байта:
>>> for i in range(len(a)):
... x = a[:i]
... print('len: {}'.format(len(x)))
... print('size: {}'.format(sys.getsizeof(x)))
...
len: 0
size: 72
len: 1
size: 80
len: 2
size: 88
Как напоминает нам Joe Pinsonault , это накладные расходы складываются. И сами целые объекты не очень большие - они в три раза больше ссылок. Таким образом, это избавляет вас от некоторой памяти в абсолютном смысле, но асимптотически может быть полезно иметь несколько списков, которые являются "представлениями" в одну и ту же память.
К сожалению, Python не обеспечивает простой способ создания объектов, которые являются "представлениями" в списках. Или, может быть, я должен сказать "к счастью"! Это означает, что вам не нужно беспокоиться о том, откуда приходит срез; изменения в оригинале не повлияют на срез. В целом, это делает рассуждение о поведении программы намного проще.
Если вы действительно хотите сохранить память, работая с представлениями, подумайте об использовании массивов numpy
. Когда вы нарезаете массив numpy
, память делится между срезом и оригиналом:
>>> a = numpy.arange(3)
>>> a
array([0, 1, 2])
>>> b = a[1:3]
>>> b
array([1, 2])
Что происходит, когда мы изменяем a
и снова смотрим на b
?
>>> a[2] = 1001
>>> b
array([ 1, 1001])
Но это означает, что вы должны быть уверены, что когда вы изменяете один объект, вы не произвольно не изменяете другого. Это компромисс при использовании numpy
: меньше работы для компьютера, и больше работы для программиста!
В зависимости от того, что вы делаете, вы можете использовать islice
.
Поскольку он работает через итерацию, он не будет создавать новые списки, а вместо этого будет просто создавать итераторы, которые yield
элементы из исходного списка будут запрошены для их диапазонов.