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

Вызов списка() опустошает мой итерируемый объект?

a = range(1, 3)
a = iter(a)
list(a)
a = list(a)

a оценивается как [ ].

a = range(1, 3)
a = iter(a)
a = list(a)

a оценивается как [1, 2].

Первый результат мне неожиданен. Какая семантика здесь происходит?

4b9b3361

Ответ 1

Проблема не list(), а iter(), которая, как задокументировано, возвращает одноразовый iterator. Как только что-то достигло элементов iterator, итератор постоянно пуст. Наиболее часто используемый iterable тип (обычно) многоразовый, и два типа не следует путать.

Обратите внимание, что вам не нужно iter(), чтобы превратить range в list, потому что list() принимает iterable в качестве аргумента:

>>> a = range(1, 3)
>>> list(a)
[1, 2]
>>> list(a)
[1, 2]

И только iterator, возвращаемый iter(), является одноразовым:

>>> b = iter(a)
>>> list(b)
[1, 2]
>>> list(b)
[]
>>> list(a)
[1, 2]

Ответ 2

Посмотрите, что происходит:

>>> a = range(1, 3)
>>> a is iter(a)
False

как вы можете видеть, iter дает новый объект итератора, который не сам a

>>>> a = iter(a)

имя a теперь соответствует выделенному объекту итератора iter, предоставленному нам (как если бы iter(a) вернул себя, например, как это происходит с zip и с файлами)

>>> list(a)
[1, 2]

исчерпывает итератор, поэтому

>>> list(a)
[]

ничего не дает, поскольку итератор уже используется (итерация) уже

Вот еще несколько экспериментов, которые вы можете попытаться полностью понять, что происходит:

>>> a = range(1, 3)
>>> a
range(1, 3)
>>> type(a)
<class 'range'>
>>> b = iter(a)
>>> b
<range_iterator object at 0x7f331a6d96c0>
>>> type(b)
<class 'range_iterator'>
>>> a is b
False
>>> list(b)
[1, 2]
>>> list(b)
[]
>>> list(a)
[1, 2]
>>> list(a)
[1, 2]
>>> a
range(1, 3)
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'range' object is not an iterator
>>> b=iter(a)
>>> next(b)
1
>>> next(b)
2
>>> next(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> z=zip(a,b)
>>> iter(z) is z
True
>>> with open('words.txt') as f:
...  iter(f) is f
... 
True

Примечание: на Python 2 довольно много функций возвращают списки вместо итераторов (например, zip)

Ответ 3

a = iter(a)
list(a)
^^^^^^^

Средство конвертирует итератор в список и не сохраняет результат. Однако итератор может производить только один раз. После того, как вы прочтете его, он станет пустым.

И если вы попробуете next() здесь, вы также увидите, что происходит:

>>> next(a)
1

>>> next(a)
2

>>> next(a)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
StopIteration

>>> list(a)
[]

Из документ:

Объект, представляющий поток данных. Повторные вызовы метода iterators __next__() (или передача его встроенной функции next()) возвращают последовательные элементы в потоке.

Когда больше нет данных, вместо этого возникает исключение StopIteration. На этом этапе объект итератора исчерпается, и любые дальнейшие вызовы его методу __next__() снова вызовут StopIteration.

Ответ 4

Создание списка из итератора потребляет итератор. Проще говоря, его элементы создаются, когда это необходимо, и содержимое итератора пуст после того, как вы повторили его. Если вам нужна другая копия итератора, например, для создания списка, вы можете использовать itertools.tee.

>>> from itertools import tee
>>> it1, it2 = tee(range(1,3))
>>> lst = list(it1)
>>> lst
[1, 2]
>>> for x in it2:
...     print(x)
... 
1
2
>>> list(it2) # it2 is now exhausted
[]

Ответ 5

list(thing) означает, что все элементы из thing (как for item in thing) выполнили бы, и сохраните все элементы в новом списке.

iter(thing) означает получить "итератор" для thing; итератор в основном является маркером в потоке элементов данных, запоминающих, где вы находитесь, чем может использоваться для получения следующего элемента из потока (продвижение "маркера" в качестве побочного эффекта). Он явно не имеет способа вернуть маркер в начало для повторения итерации; это значит, что он может поддерживать итерацию вещей, которые по своей сути не могут повторяться несколько раз.

Итерирование всех элементов из thing (as for item in thing) получает итератор для thing, а затем использует итератор для извлечения всех элементов из этой вещи. Поэтому, если вы это сделаете:

a = range(1, 3)
for x in a:
    print x
for x in a:
    print x

Цикл for создает новый итератор для a (который начинается с маркера в начале диапазона), затем вытягивает элементы из итератора, пока он не убежит от конца диапазона. Сам объект диапазона нетронутый, поэтому второй цикл цикла может создать новый итератор (начиная с начала снова) и повторить его снова.

Но здесь:

a = range(1, 3)
a = iter(a)
for x in a:
    print x
for x in a:
    print x

Вы не позволяете циклу for создавать итератор для диапазона, вместо этого вы делаете это явно и только один раз. Когда цикл for отправляется для создания итератора из a, он получает только сам a (iter(i), когда i является итератором, всегда требуется вернуть i). Таким образом, цикл for тянет элементы из a, каждый раз продвигая маркер, пока маркер не будет "выключен" объекта диапазона.

Затем второй цикл for делает итератор из итератора a и снова получает a. Затем он извлекает элементы из этого итератора, пока не закончится; который может выполнять нулевые времена, потому что a уже "выключен".

Таким образом, это фактически не имеет ничего общего с вашими вызовами list, это именно то, как объекты итератора, которые вы получаете с помощью iter behave. Обычно вы не используете iter очень много, потому что вещи, которые вы используете для итерации коллекций (для циклов, list() и т.д.), Уже обрабатывают создание итераторов для вас. Вы использовали бы только iter, когда делаете что-то сложное, в том числе частично потребляя итератор, а затем потребляете больше элементов, начиная с того момента, когда первая частичная итерация прекратилась.

Ответ 6

iter() возвращает итератор, а второй вызов list() возвращает пустую последовательность.

>>> a = iter(range(1,3))
>>> list(a)
[1, 2]
>>> list(a)
[]

Документы о iterable:

Когда итерируемый объект передается как аргумент встроенной функции iter(), он возвращает итератор для объекта. Этот итератор хорош для одного прохода над набором значений.

Документы о iterator:

Объект-контейнер (например, список) создает новый новый итератор каждый раз, когда вы передаете его функции iter() или используете его в цикле for. Попытка этого с помощью итератора просто вернет тот же исчерпанный объект итератора, который использовался в предыдущем проходе итерации, , чтобы он выглядел как пустой контейнер.