检测对象是否可重复迭代

max*_*max 7 python iterator python-3.x

是否obj == iter(obj)意味着obj不能重复迭代,反之亦然?我没有在文档中看到任何这样的措辞,但根据这个评论,标准库通过测试检查对象是否可重复迭代 :if iter(obj) is obj

@agf:Python标准库的某些部分依赖于规范的这一部分; 它们通过测试检测某些东西是否是迭代器/生成器if iter(obj) is obj:,因为真正的迭代器/生成器对象将__iter__定义为标识函数.如果测试为真,则转换为list允许重复迭代,否则,假设对象可重复迭代,并且可以按原样使用它.
- ShadowRanger 6月3日17:23

文档确实声明如果obj是迭代器,则需要iter(obj)返回obj.但我认为这并不足以得出结论,可以使用非重复可迭代对象来识别iter(obj) is obj.

Zer*_*eus 5

所有迭代器都是迭代器,但并非所有迭代器都是迭代器.

迭代的唯一要求是它定义了一个__iter__()返回迭代器的方法:

需要为容器对象定义一个方法以提供迭代支持:

container.__iter__()
返回一个迭代器对象.

一个迭代器必须遵循迭代器协议,其中有两个要求:

  1. 它有一个返回对象本身__iter__()方法:

    iterator.__iter__()
    返回迭代器对象本身.

  2. 它有一个__next__()方法可以在每次调用时返回下一个项目,并且一旦用尽,就会StopIteration 在每次后续调用时引发:

    一旦迭代器的__next__()方法引发StopIteration,它必须继续在后续调用中这样做.不遵守此属性的实现被视为已损坏.

这些要求意味着迭代器永远不会重复,而且,你总是可以确认一个可迭代通过确认是一个迭代器(因此根据定义不可重复的)iter(obj) is objTrue:

def is_unrepeatable(obj):
    return iter(obj) is obj
Run Code Online (Sandbox Code Playgroud)

但是:由于iterable的唯一要求是iter(obj)返回一些迭代器,因此无法证明它可重复的.一个iterable可以定义一个__iter__()方法,每次调用它时返回一个具有不同行为的迭代器:例如,它可以返回一个迭代器,它在第一次调用时迭代它的元素,但是在后续调用中,返回一个立即引发的迭代器StopIteration.

这种行为会很奇怪(而且很烦人),但并不是禁止的.这是一个不可重复的可迭代类的例子,它不是迭代器:

class Unrepeatable:

    def __init__(self, iterable):
        self.iterable = iterable
        self.exhausted = False

    def __iter__(self):
        if self.exhausted:
            return
        else:
            self.exhausted = True
            yield from self.iterable
Run Code Online (Sandbox Code Playgroud)

>>> x = Unrepeatable([1,2,3])
>>> list(x)
[1, 2, 3]
>>> list(x)
[]
>>> iter(x) is x
False
>>> 
Run Code Online (Sandbox Code Playgroud)

我会毫不犹豫地称这样一个"伪造的迭代器"表现得很糟糕,我想不出你在野外找到一个的情况,但如上所述,它是可能的.

  • 更进一步:虽然提出一个病态的反例是微不足道的,但我认为可以安全地假设"`iter(obj)不是obj` <=> obj在实践中是可重复的.在这个问题上,由核心python开发人员参见[讨论](https://mail.python.org/pipermail/python-ideas/2013-September/023241.html).我没有看到一个不可重复的非迭代器迭代的例子,它既不是无用的也不是无意义的. (2认同)
  • @shadydog:至少在Python 2.7上,`urllib.urlopen`是一个现实世界的反例.它返回一个对象,其中`iter(obj)不是obj`,而是具有类似文件的一次性迭代行为.我不知道Python 3的等效行为是否相同.`iter(x)is iter(x)`是一个稍微更可靠的检查,捕获`urllib.urlopen`; 我不知道任何人实际使用它的失败,但它仍然无法捕捉到一切. (2认同)