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]
.
Первый результат мне неожиданен. Какая семантика здесь происходит?
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]
.
Первый результат мне неожиданен. Какая семантика здесь происходит?
Проблема не 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]
Посмотрите, что происходит:
>>> 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
)
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.
Создание списка из итератора потребляет итератор. Проще говоря, его элементы создаются, когда это необходимо, и содержимое итератора пуст после того, как вы повторили его. Если вам нужна другая копия итератора, например, для создания списка, вы можете использовать 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
[]
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
, когда делаете что-то сложное, в том числе частично потребляя итератор, а затем потребляете больше элементов, начиная с того момента, когда первая частичная итерация прекратилась.
iter()
возвращает итератор, а второй вызов list()
возвращает пустую последовательность.
>>> a = iter(range(1,3))
>>> list(a)
[1, 2]
>>> list(a)
[]
Документы о iterable:
Когда итерируемый объект передается как аргумент встроенной функции iter(), он возвращает итератор для объекта. Этот итератор хорош для одного прохода над набором значений.
Документы о iterator:
Объект-контейнер (например, список) создает новый новый итератор каждый раз, когда вы передаете его функции iter() или используете его в цикле for. Попытка этого с помощью итератора просто вернет тот же исчерпанный объект итератора, который использовался в предыдущем проходе итерации, , чтобы он выглядел как пустой контейнер.