首页 > 解决方案 > 何时使用 __iter__() 与 iter()?

问题描述

假设我有一个实现__iter__()函数的类,是首选使用iter(obj)还是直接调用obj.__iter__()?除了必须使用魔法功能少输入 5 个字符之外,还有什么真正的区别吗?

相比之下:对于我来说next()__next__()我可以看到使用魔法函数具有默认值的优势。

标签: python

解决方案


区别主要只是方便。打字更少,阅读符号更少,阅读速度更快。但是,各种内置函数(例如iterlen等)通常会进行一些类型检查以及早发现错误。如果您编写了一个客户__iter__方法并且它返回 2了,那么调用obj.__iter__()不会捕获到它,而是iter(obj)会抛出一个类型错误。例如。

>>> class X:
    def __iter__(self):
        return 2

>>> x = X()
>>> x.__iter__()
2
>>> iter(x)
Traceback (most recent call last):
  File "<pyshell#37>", line 1, in <module>
    iter(x)
TypeError: iter() returned non-iterator of type 'int'

iter还为没有 的对象实现了迭代器协议__iter__,但实现了序列协议。也就是说,他们有一个__getitem__方法可以实现从索引 0 开始的序列,并为不在范围内的索引引发 IndexError。这是 python 的一个较旧的特性,而不是真正应该使用的新代码。例如。

>>> class Y:
    def __getitem__(self, index):
        if 0 <= index < 5:
            return index ** 2
        else:
            raise IndexError(index)

>>> list(iter(Y()))  # iter not strictly needed here
[0, 1, 4, 9, 16]

你应该什么时候使用__iter__?这可能与 没有那么相关__iter__,但是如果您需要访问父类使用的方法的实现,那么最好以样式调用此类方法super().__<dunder_method>__()(使用 Python 3 样式超级用法)。例如。

>>> class BizzareList(list):
    def __iter__(self):
        for item in super().__iter__():
            yield item * 10

>>> l = BizzareList(range(5))
>>> l  # normal access
[0, 1, 2, 3, 4]
>>> l[0]  # also normal access
0
>>> tuple(iter(l))  # iter not strictly needed here
(0, 10, 20, 30, 40)

推荐阅读