#python #list #iterator
#python #Список #итератор
Вопрос:
Допустим, у нас есть следующий список, и мы создаем для него итератор:
lst = [1,2,3]
itr = iter(lst)
Далее предположим, что мы меняем наш список на совершенно другие значения:
lst = ['a', 'b', 'c']
И если я, мы запустим следующий цикл:
for x in itr:
print x
Мы получим '1,2,3'
. Но почему? Насколько я понимаю, итератор копирует не все значения из итерирующего объекта. По крайней мере, итератор для списка из трех элементов имеет тот же размер, что и список из 100000 элементов. sys.getsizeof(i)
ВОЗВРАТ 64
. Как итератор может быть таким маленьким по размеру и сохранять «старые» значения списка?
Комментарии:
1. Вы не изменили список, вы создали новый, совершенно не связанный.
Ответ №1:
Сам итератор содержит ссылку на список. Поскольку lst
вместо mutated используется rebound, эта ссылка не меняется.
>>> lst = [1, 2, 3]
>>> itr = iter(lst)
>>> lst[:] = ['a', 'b', 'c']
>>> for x in itr:
... print x
...
a
b
c
Ответ №2:
Итератор ссылается на объект списка, а не на имя. Таким образом, переназначение имени lst
другому объекту никак не влияет на итератор; имена привязаны к объектам и ссылаются на объекты, но имена не являются самим объектом.
Вы можете получить просмотр объекта, на который ссылается итератор gc.get_referents
:
>>> import gc
>>> lst = [1,2,3]
>>> itr = iter(lst) # return an iterator for the list
>>> lst = ['a', 'b', 'c'] # Bind name lst to another object
>>> gc.get_referents(itr)[0]
[1, 2, 3]
Как вы заметите, итератор по-прежнему ссылается на первый объект list.
Следующая ссылка поможет вам узнать больше об именах и привязке в Python:
Ответ №3:
Добро пожаловать в систему ссылок на объекты Python. Имена переменных на самом деле не имеют глубокой связи с фактическим объектом, хранящимся в памяти.
lst = [1, 2, 3]
itr = iter(lst) # iter object now points to the list pointed to by lst
print(next(itr)) # prints 1
# Both `lst` and `lst1` now refer to the same list
lst1 = lst
# `lst` now points to a new list, while `lst1` still points to the original list.
lst = ['a', 'b', 'c']
print(next(itr)) # prints 2
lst.append(4)
lst1.append(5) # here the list pointed to by `itr` is updated
for i in itr:
print(i) # prints 3, 5
TL; DR: Имена переменных Python — это просто теги, которые ссылаются на некоторый объект в пространстве.
Когда вы вызываете iter
list
именованный lst
объект iterator указывает на фактический объект, а не на имя lst
.
Если вы можете изменить исходный объект, вызвав append
, extend
, pop
, remove
и т.д., Это повлияет на результат итератора. Но когда вы присваиваете новое значение lst
, создается новый объект (если он ранее не существовал) и lst
просто начинает указывать на этот новый объект.
Сборщик мусора удалит исходный объект, если ни один другой объект не указывает на него ( itr
указывает на него в данном случае, поэтому исходный объект еще не будет удален).
http://foobarnbaz.com/2012/07/08/understanding-python-variables/
Дополнительно:
lst1.extend([6, 7, 8])
next(itr) # raises StopIteration
Это не имеет ничего общего со ссылками на объекты, итератор просто сохраняет внутри, что он повторил полный список.