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

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

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

6 Solutions collect form web for “Вызов списка () опустошает мой итерируемый объект?”

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

Обратите внимание, что вам не нужен 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(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) [] 

Из документа :

Объект, представляющий поток данных. Повторные вызовы метода __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 (как for item in thing ), получает итератор для thing , а затем использует итератор для извлечения всех предметов из этой вещи. Поэтому, если вы это сделаете:

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

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

Но здесь:

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

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

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

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

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

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

Документы об истребителе :

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

Документы об итераторе :

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

  • Разница между использованием и list () в Python
  • Назначение списка с помощью
  • Python: как отделить строку запятой
  • Петли Python с несколькими списками?
  • Python 2: различное значение ключевого слова «in» для наборов и списков
  • Python присваивает нескольким переменным одинаковое значение? поведение списка
  • Ярлык OR-chain применяется в списке
  • как извлечь вложенные списки?
  • Python - лучший язык программирования в мире.