微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Python for循环和迭代器行为

如何解决Python for循环和迭代器行为

您的怀疑是正确的:迭代器已被消耗。

实际上,您的迭代器是一个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 in接受一个可迭代的对象作为其第二个参数。
  • 在您的示例示例中,可迭代对象可以是生成器,但也可以是任何其他可迭代对象,例如list,或dict,或str对象(字符串)或提供所需功能用户定义类型。
  • iter函数将应用于对象以获取迭代器(顺便说一句:iter正如您所做的那样,不要在Python中用作变量名-它是关键字之一)。实际上,更准确地说,是调用对象的__iter__方法(在大多数情况下,所有iter函数都可以执行;这__iter__是Python所谓的“魔术方法”之一)。
  • 如果调用__iter__成功,则next()循环将函数一次又一次地应用于可迭代对象,并将提供给的第一个变量for in分配给next()函数的结果。(请记住:可迭代对象可以是生成器,也可以是容器对象的迭代器,或任何其他可迭代对象。)实际上,更准确地说:它调用了迭代器对象的__next__方法,这是另一个“魔术方法”。
  • for当循环结束next()引发StopIteration异常(当可迭代不具有另一个目的是产生时通常发生next()调用)。

您可以通过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会产生与产生不同的效果forin,但是,了解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__方法

  • 如果对象没有__iter__返回迭代器的方法in则使用对象的__getitem__方法2退回到旧式迭代协议。
  • 如果上述所有尝试均失败,您将获得一个TypeError异常

如果您希望创建自己的对象类型以进行迭代(即,您可以使用,也可以forinin在其上使用),那么了解生成器中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中(没有下划线)

2请参 见此答案以了解创建可迭代类的不同方法

解决方法

我想了解更多有关的信息iterators,所以如果我错了,请纠正我。

迭代器是一个对象,该对象具有指向下一个对象的指针,并作为缓冲区或流(即,链表)读取。它们特别有效,因为它们所做的只是通过引用而不是使用索引来告诉您下一步是什么。

但是我仍然不明白为什么会发生以下行为:

In [1]: iter = (i for i in range(5))

In [2]: for _ in iter:
   ....:     print _
   ....:     
0
1
2
3
4

In [3]: for _ in iter:
   ....:     print _
   ....:

In [4]:

经过迭代器(In [2])的第一个循环后,就好像它已被消耗并且留空,因此第二个循环(In [3])不输出任何内容。

但是,我从未为iter变量分配新值。

for循环幕后到底发生了什么?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。