Как на самом деле работает итератор python для списка?

#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
  

Это не имеет ничего общего со ссылками на объекты, итератор просто сохраняет внутри, что он повторил полный список.