【问题标题】:Why is an iterable object not an iterator?为什么可迭代对象不是迭代器?
【发布时间】:2015-11-27 11:06:59
【问题描述】:

这是我的代码:

from collections import deque

class linehistory:
    def __init__(self, lines, histlen=3):
        self.lines = lines
        self.history = deque(maxlen=histlen)

    def __iter__(self):
        for lineno, line in enumerate(self.lines,1):
            self.history.append((lineno, line))
            yield line

    def clear(self):
        self.history.clear()


f = open('somefile.txt')
lines = linehistory(f)
next(lines)

错误:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
    TypeError: 'linehistory' object is not an iterator

我不知道为什么 linehistory 对象不是迭代器,因为它已经在 the 类中包含了 __iter__ 方法。

【问题讨论】:

标签: python iterator


【解决方案1】:

迭代的概念有据可查in the Python documentation

简而言之,“可迭代”是我要迭代的对象,也称为容器。这可以是一个列表、一个字符串、一个元组或任何其他包含或可以产生多个项目的东西。它有__iter__(),它返回一个迭代器。

“迭代器”是用于一次迭代的对象。它可以看作是一种“光标”。它有 next()(在 Python 2 中)或 __next__()(在 Python 3 中),它们会被重复调用,直到引发 StopIteration 异常。因为任何迭代器也是可迭代的(作为它自己的迭代器),它也有 __iter__() 来返回自己。

您可以使用iter(obj) 获取任何可迭代对象的迭代器。

在你的例子中,linehistory(应该写成LineHistory)是可迭代的,因为它有一个.__iter__()。用 this 创建的生成器对象是一个迭代器(就像每个生成器对象一样)。

【讨论】:

    【解决方案2】:

    其实

    所有这些其他答案都是错误的(@glglgl 的写作风格迟钝除外)。如果您使用 for 循环调用它,您的生成器函数 __iter__() 将按原样工作

    for line in lines:
        print(line)
    

    但是因为你使用了next(lines),所以你必须首先使用iter() 来获取迭代器(我假设它只是在对象上调用 __iter__())

    it = iter(lines)
    print(next(it))
    

    作为Mr.Beazley points out

    【讨论】:

    • “文笔迟钝的glglgl”你能详细说明一下吗?
    • @glglgl 你说同样的事情两次,它是否会带来清晰,即"iterable" is the object I want to iterate over和再次"iterator" is the object which is used for iteration。你介绍next 并解释它是什么,然后用生成器再做一次。您基本上假设读者知道什么:迭代、next() 和生成器。但主要是您没有提供示例,也没有对所介绍的概念提供参考。
    • 感谢您的评论。我增加了一些清晰度(希望如此),并提供了一个链接来帮助读者找到“原始”定义。
    • 在我的例子中(来自你的第一个例子),line 是一个 dict 对象。我怎样才能从中提取密钥(而不遍历所有内容)?
    • @LShaver 如果您在 dict 上使用 for 循环,您只会得到密钥。您还可以在dict 上使用.keys() 并获得一个可迭代的键,其工作方式类似于上面示例中的lines
    【解决方案3】:

    我不知道为什么 linehistory 对象不是迭代器,因为它已经在类中包含 __iter__ 方法。

    错了。见Iterator Types

    迭代器对象本身需要支持以下两种方法,它们共同构成了迭代器协议:

    iterator.__iter__()
    返回迭代器对象本身。这是允许容器和迭代器与 for 和 in 语句一起使用的必要条件。此方法对应 Python/C API 中 Python 对象的类型结构的 tp_iter 槽。

    iterator.__next__()
    从容器中返回下一个项目。如果没有其他项目,则引发 StopIteration 异常。此方法对应 Python/C API 中 Python 对象的类型结构的 tp_iternext 槽。

    但是你可以迭代lines,那是因为你的__iter__方法是一个生成器函数,见Generator Types

    Python 的生成器提供了一种方便的方式来实现迭代器协议。如果容器对象的__iter__() 方法被实现为生成器,它将自动返回提供__iter__()__next__() 方法的迭代器对象(技术上是生成器对象)。有关生成器的更多信息,请参阅 yield 表达式的文档。

    【讨论】:

      【解决方案4】:

      迭代器对象需要 __iter__ 方法,但它们还需要实现 next

      迭代器对象本身需要支持以下两种方法,它们共同构成了迭代器协议:

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

      迭代器.next()
      从容器中返回下一个项目。

      Python 2.7 Source

      在 Python 3.x 中,这些是函数名:

      迭代器.__iter__()

      迭代器.__next__()

      Python 3.x Source

      【讨论】:

        【解决方案5】:

        你的对象它不是一个迭代器,就像一个列表它不是一个迭代器,而是一个可迭代对象。不过,您可以将其设为迭代器。因为迭代器本身就是一个对象。

        help(list)
        

        然后:

         |  __iter__(self, /)
         |      Implement iter(self).
        

        假设你有一个列表:

        a = [1,2,3]
        

        然后你尝试打电话

        next(a)
        

        你得到:

        Traceback (most recent call last):
          File "<pyshell>", line 1, in <module>
        TypeError: 'list' object is not an iterator
        

        但是您可以通过在其上应用 iter() 来创建列表的迭代器。

        >>> iter_a = iter(a)
        >>> print(iter_a)
        <list_iterator object at 0x03FE8FB0>
        
        >>> next(iter_a)
        1
        

        所以用你的代码来做这个:

        f = open('somefile.txt')
        lines = linehistory(f)
        lines_iter = lines.__iter__()
        print(next(lines_iter))
        

        somefile.txt 的第一行(我的文件):

        >>> %Run 'some file iterator.py'
        aaaaaaa
        

        现在做:

        >>> dir(lines_iter)
        

        你会看到:

         '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', 
        

        看到了吗?它现在有一个 next 方法!

        【讨论】:

          猜你喜欢
          • 2018-08-16
          • 2020-10-22
          • 2021-03-01
          • 2021-05-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-29
          • 2011-02-05
          相关资源
          最近更新 更多