Подтвердить что ты не робот

Нарезка списка на Python без создания копии

У меня есть следующая проблема.

Учитывая список целых чисел L, мне нужно сгенерировать все подписи L[k:] for k in [0, len(L) - 1], без генерации копий.

Как это сделать в Python? С каким-то буферным объектом?

4b9b3361

Ответ 1

Короткий ответ

Списки разреза не генерируют копии объектов в списке; он просто копирует ссылки на них. Это ответ на заданный вопрос.

Длинный ответ

Тестирование по изменяемым и неизменяемым значениям

Сначала давайте протестируем основное утверждение. Мы можем показать, что даже в случае неизменяемых объектов, таких как целые числа, копируется только ссылка. Вот три разных целочисленных объекта, каждый с одинаковым значением:

>>> 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: меньше работы для компьютера, и больше работы для программиста!

Ответ 2

В зависимости от того, что вы делаете, вы можете использовать islice.

Поскольку он работает через итерацию, он не будет создавать новые списки, а вместо этого будет просто создавать итераторы, которые yield элементы из исходного списка будут запрошены для их диапазонов.