调用list()会清空我的可迭代对象吗?

时间:2016-01-11 22:54:53

标签: python list

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 个答案:

答案 0 :(得分:25)

问题不是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]

答案 1 :(得分:5)

让我们来看看会发生什么:

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

答案 2 :(得分: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)
[]

来自the document

  

表示数据流的对象。重复调用迭代器的__next__()方法(或将其传递给内置函数next())将返回流中的连续项。

     

当没有更多数据可用时,会引发StopIteration异常。此时,迭代器对象已耗尽,对__next__()方法的任何进一步调用只会再次引发StopIteration。

答案 3 :(得分:2)

从迭代器创建列表消耗迭代器。简单地说,它的元素是在需要时创建的,并且在迭代之后迭代器的内容是空的。如果您想要迭代器的另一个副本,例如创建列表,则可以使用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
[]

答案 4 :(得分:2)

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创建一个新的迭代器(以范围开头的标记开始),然后从迭代器中提取项目,直到它从范围的末尾开始运行。范围对象本身是不变的,因此第二个for循环可以创建一个新的迭代器(从头重新开始)并再次迭代它。

但是在这里:

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是迭代器时总是需要返回{{1} }})。因此,for循环从i中提取项目,每次推进标记,直到标记为范围对象的“结束”。

然后第二个for循环从迭代器a创建一个迭代器,并再次获得a。然后它从该迭代器中拉出项目直到它用完为止;它可以做零次,因为a已经“结束”了。

所以它实际上与你的a调用无直接关系,它只是你用list得到的迭代器对象的行为。通常你不会非常使用iter,因为你用来迭代集合的东西(for循环,iter等)已经为你处理迭代器的创建。当你做一些涉及部分使用迭代器的复杂事情,然后从第一次部分迭代停止的地方开始消耗更多项目时,你只会使用list()

答案 5 :(得分:2)

iter()返回一个itera tor ,第二次调用list()会返回一个空序列。

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

关于iterable的文档:

  

当一个可迭代对象作为参数传递给内置函数iter()时,它返回该对象的迭代器。 这个迭代器适用于一组值的传递。

关于iterator的文档:

  

每次将容器对象(例如列表)传递给iter()函数或在for循环中使用它时,它都会生成一个全新的迭代器。 使用迭代器尝试此操作将返回上一次迭代过程中使用的相同耗尽迭代器对象,使其看起来像一个空容器