【问题标题】:Detecting that an object is repeatedly iterable检测对象是可重复迭代的
【发布时间】:2017-03-19 15:10:28
【问题描述】:

obj == iter(obj) 是否暗示 obj 不能重复迭代,反之亦然?我在文档中没有看到任何这样的措辞,但根据 this comment,标准库检查对象是否可重复迭代 by testing if iter(obj) is obj

@agf:Python 标准库的某些部分依赖于规范的这一部分; they detect whether something is an iterator/generator by testing if iter(obj) is obj:,因为真正的迭代器/生成器对象将 __iter__ 定义为标识函数。如果测试为真,它们将转换为list 以允许重复迭代,否则,假定对象是可重复迭代的,它们可以按原样使用它。
——ShadowRangerJun 3 at 17:23

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

【问题讨论】:

    标签: python python-3.x iterator


    【解决方案1】:

    所有的迭代器都是可迭代的,但并非所有的可迭代都是迭代器。

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

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

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

    iterator 必须遵循迭代器协议,它有两个要求:

    1. 它有一个__iter__() 方法that returns the object itself

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

    2. 它有一个__next__() 方法,该方法在每次调用时返回下一项,一旦用尽,就会引发StopIteration on every subsequent call

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

    这些要求意味着迭代器永远不可重复,并且您始终可以通过确认 iter(obj) is objTrue 来确认可迭代器是迭代器(因此根据定义不可重复):

    def is_unrepeatable(obj):
        return iter(obj) is obj
    

    然而:因为迭代的唯一要求是iter(obj)返回一些迭代器,你不能证明它是可重复的。一个可迭代对象可以定义一个 __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
    

    >>> x = Unrepeatable([1,2,3])
    >>> list(x)
    [1, 2, 3]
    >>> list(x)
    []
    >>> iter(x) is x
    False
    >>> 
    

    我会毫不犹豫地称这样的“假迭代器”行为不端,而且我想不出你会在野外找到一个,但如上所示,这是可能的。

    【讨论】:

    • 更进一步:虽然想出一个病态的反例是微不足道的,但我认为在实践中假设“iter(obj) is not obj obj 是可重复的”是安全的。在这个问题上,请参阅核心 python 开发人员的 discussion。我还没有看到一个不可重复的、不可迭代的可迭代的例子,它也不是无用的或无意义的。
    • @shadydog:至少在 Python 2.7 上,urllib.urlopen 是一个真实的反例。它返回一个带有iter(obj) is not obj 的对象,但具有类似文件的一次性迭代行为。我不知道 Python 3 等价物的行为是否相同。 iter(x) is iter(x) 是一个更可靠的检查,可以捕获 urllib.urlopen;我不知道任何人实际使用它失败的任何东西,但它仍然无法捕获所有内容。
    猜你喜欢
    • 2018-09-20
    • 2015-10-23
    • 2018-11-26
    • 1970-01-01
    • 2021-10-27
    • 2022-12-05
    • 2013-07-13
    • 2014-04-16
    • 1970-01-01
    相关资源
    最近更新 更多