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



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




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




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

589   2  

2 ответов:

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

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

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

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

Во-первых, давайте проверим основное утверждение. Мы можем показать, что даже в случае неизменяемых объектов, таких как целые числа, копируется только ссылка. Вот три различных целочисленных объекта, каждый с тем же самым значение:
>>> a = [1000 + 1, 1000 + 1, 1000 + 1]
Они имеют одинаковое значение, но вы можете видеть, что это три различных объекта, потому что они имеют разные ids:
>>> map(id, a)
[140502922988976, 140502922988952, 140502922988928]

Когда вы их срезаете, ссылки остаются теми же. Новые объекты не были созданы:

>>> b = a[1:3]
>>> map(id, b)
[140502922988952, 140502922988928]

Использование различных объектов с одинаковым значением показывает, что процесс копирования не утруждает себяинтернированием - он просто непосредственно копирует ссылки.

Тестирование с изменяемыми значениями дает то же самое результат:

>>> 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

, как Джо 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 элементы из исходного списка, как это требуется для их диапазонов.

Comments

    Ничего не найдено.