您的怀疑是正确的:迭代器已被消耗。
实际上,您的迭代器是一个generator,这是一个对象,它 只能 被迭代 一次。
type((i for i in range(5))) # says it's type generator
def another_generator():
yield 1 # the yield expression makes it a generator, not a function
type(another_generator()) # also a generator
它们高效的原因与“参考”告诉您下一步是什么无关。它们之所以有效,是因为它们仅根据请求生成下一个项目。所有项目都不是一次生成的。实际上,您可以拥有一个无限生成器:
def my_gen():
while True:
yield 1 # again: yield means it is a generator, not a function
for _ in my_gen(): print(_) # hit ctl+c to stop this infinite loop!
其他一些更正可以帮助您增进理解:
您可以通过for
这种方式“手动”在python中实现循环(可能并不完美,但足够接近):
try:
temp = iterable.__iter__()
except AttributeError():
raise TypeError("'{}' object is not iterable".format(type(iterable).__name__))
else:
while True:
try:
_ = temp.__next__()
except StopIteration:
break
except AttributeError:
raise TypeError("iter() returned non-iterator of type '{}'".format(type(temp).__name__))
# this is the "body" of the for loop
continue
实际上,for
循环中最有趣的部分不是for
,而是in
。单独使用in
会产生与产生不同的效果for
in
,但是,了解in
使用其参数的作用非常有用,因为它for
in
实现了非常相似的行为。
单独使用时,in
关键字首先调用对象的__contains__
method,这是另一个“魔术方法”(请注意,使用时将跳过此步骤for
in
)。使用in
一个容器本身,你可以做这样的事情:
1 in [1, 2, 3] # True
‘He’ in ‘Hello’ # True 3 in range(10) # True ‘eH’ in ‘Hello’[::-1] # True
如果可迭代对象不是容器(即它没有__contains__
方法),则in
下一步尝试调用该对象的__iter__
方法。如前所述:该__iter__
方法返回Python中称为iterator的值。基本上,迭代器是一个对象,您可以next()
在1上使用内置的泛型函数。生成器只是迭代器的一种类型。
如果__iter__
成功调用,则in
关键字将函数next()
一次又一次地应用于可迭代对象。(请记住:可迭代对象可以是生成器,也可以是容器对象的迭代器,或任何其他可迭代对象。)实际上,更确切地说,它调用迭代器对象的__next__
方法。
如果您希望创建自己的对象类型以进行迭代(即,您可以使用,也可以for
in
仅in
在其上使用),那么了解生成器中yield
使用的关键字(如上所述)很有用。
class MyIterable():
def __iter__(self):
yield 1
m = MyIterable()
for _ in m: print(_) # 1
1 in m # True
将yield
函数或方法变成生成器而不是常规函数/方法的存在。__next__
如果使用生成器(它会__next__
自动提供),则不需要该方法。
如果您希望创建自己的容器对象类型(即可以单独使用in
它,但不能使用for
in
),则只需要该__contains__
方法即可。
class MyUselessContainer():
def __contains__(self, obj):
return True
m = MyUselessContainer()
1 in m # True
'Foo' in m # True
TypeError in m # True
None in m # True
1请注意,要成为迭代器,对象必须实现迭代器协议。这仅意味着__next__
和__iter__
方法都必须 正确 实现(生成器带有“免费”此功能,因此您在使用它们时无需担心)。还要注意,该___next__
方法实际上next
在Python 2中(没有下划线)。