【问题标题】:Why does a Python Iterator need an iter method that simply returns self?为什么 Python 迭代器需要一个只返回 self 的 iter 方法?
【发布时间】:2014-02-16 01:49:36
【问题描述】:

我了解标准规定确实如此,但我正在努力寻找其根本原因。

如果它总是简单地返回self,那还需要什么?

您显然始终可以访问该对象,因为您在该对象上调用iter,那么拥有它有什么需要?

【问题讨论】:

    标签: python iterator


    【解决方案1】:

    想象一下,您想编写对任何类型的可迭代对象进行迭代的代码。通常你只需要写一个for 声明或理解,但让我们明确地做for 在幕后所做的事情,让事情更明显:

    i = iter(iterable)
    while True:
        try:
            val = next(i)
        except StopIteration:
            break
        else:
            do_stuff(val)
    

    如果您不执行该第一行,您的代码将无法处理列表、字符串、元组或除迭代器之外的任何内容。

    但是,如果您执行第一行,那么iter(iterable) 最好返回一个迭代器,否则您的代码将无法使用迭代器。


    您可能想知道为什么iter 没有这个就不能做正确的事?毕竟,当给定一个具有__len____getitem__ 但没有__iter__ 的对象时,它确实具有创建迭代器的魔力,那么为什么它不能也具有魔力呢?如果它有__next__ 但没有__iter__,则只返回它的参数?这是一个语言设计问题,但一般来说,Python 会尽量减少魔法。使不完全序列可迭代的魔法是必要的,因为在将迭代协议添加到语言之前,这样的序列就已经存在(在广泛的第三方代码中),而且为了简化事情的小小好处,删除它太难了。

    【讨论】:

    • @delnan:答案已经说:“您通常只使用 for 语句(或理解)......它在幕后执行此代码。”需要说清楚吗?
    • 我想我只是错过了那部分。这可能表明它需要更多地突出显示,或者我需要更仔细地阅读:-)
    • @delnan:好的,新版本更好吗?
    • 如果只是略过它,肯定更难错过。但这只是锦上添花,从第一次修订开始,答案就已经很好了。 +1
    • 现在说得通了 - espc。你最后一点关于删除的困难与简化的好处。感谢您的详尽解释。
    【解决方案2】:

    for 循环应该与可迭代对象一起工作,因此for i in something: 自动调用iter(something) 来获取迭代器并迭代所述迭代器。现在,如果迭代器也不是可迭代的(即没有定义 __iter__),则不能将 for 循环与迭代器一起使用,即:

    items = [1, 2, 3]
    # this would work
    for item in items: pass
    # this wouldn't
    it = iter(items)
    for item in it: pass
    

    因此,迭代器也应该是可迭代的。另一种方法是“以某种方式检测”迭代器而不是对它们调用iter,这种方法既笨拙又脆弱(你会如何决定呢?)。

    【讨论】:

    • 你决定的方式——就像在 python-ideas 上每年建议(和拒绝)一次或两次一样——很简单:如果i.__iter__() 提出AttributeError,在做回退之前对于基于索引的迭代,首先检查i.__next__是否存在,如果存在,则返回i
    • @abarnert 而且,由于缺乏更好的措辞,这不就是“骇人听闻的和脆弱的”吗? ;-)
    • @abarnert "magical" 是我所说的 "hacky" 的意思之一。至于脆性,我至少可以看到一个不良后果:一个不平凡的__iter__ 中的真正错误,恰好表现为AttributeError,将导致Python 抱怨缺少__next__ 应该是什么可迭代。我在类似的“异常 X 意味着它不是 Y 鸭子”策略中遇到了糟糕的诊断问题:print(*y()) 其中def y(): raise TypeError; yield 声称argument after * must be a sequence, not generator
    • 但是执行检查和回退的代码将在PyObject_GetIter 中,而不是在调用它的代码中。因此,它在外部混淆了这两种错误情况这一事实不会产生任何影响。你可以看到 C-API tp_iter fallback here 和 Python-object __iter__ 代码 here。在这两种情况下,您都可以区分 tp_iter/__iter__ 不存在与现有和提高。
    • 我相信this 是调用iter 所涉及的整个机器的一个相当忠实的纯Python 端口,而this 是你如何修补它以允许迭代器不定义@ 987654348@。 (假设您不希望 C 迭代器回退到 tp_next,并且您不关心整个 iternext 优化/保护机制,并且您更关心 CPython 的 C 代码的纯 Python 端口,而不是类似的东西,比如说,PyPy 的代码……)
    【解决方案3】:

    for 循环和其他需要使用可迭代对象的代码可以无条件地调用 iter 在它们正在迭代的事物上,而不是分别处理迭代器和其他可迭代对象。特别是,非迭代器可能有一个名为next 的方法,我们希望能够将它们与迭代器区分开来。

    【讨论】:

    • 这并不是因为非迭代器可能有一个名为next 的方法。 (这可能是next被重命名为__next__的原因,当然。)
    【解决方案4】:

    __iter__() 旨在返回对象上的迭代器。什么是对已经是迭代器的对象的迭代器? self,当然。

    【讨论】:

    • 一个迭代器是一个迭代器,不管它是一个迭代器,而不是它本身,就像列表包含其他值,而不是(必然)它本身一样。无论如何,为什么要定义iter(an_iterator)?为什么必须被定义? (有充分的理由,但你没有说出任何理由)。
    猜你喜欢
    • 2021-01-20
    • 2017-03-19
    • 1970-01-01
    • 2018-03-19
    • 1970-01-01
    • 2017-04-16
    • 2021-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多