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]
。
第一个结果对我来说意外。这里有什么语义?
答案 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)