为什么在 Python 中有一个 __iter__ 方法?

Kam*_*cki 4 python iterator

为什么要有__iter__方法?如果一个对象是一个迭代器,那么拥有一个返回自身的方法是没有意义的。如果它不是一个迭代器而是一个可迭代的,即带有__iter__and__getitem__方法的东西,那么为什么要定义一些返回迭代器但不是迭代器本身的东西呢?在 Python 中,何时需要定义一个本身不是迭代器的可迭代对象?或者,什么是可迭代但不是迭代器的示例?

tim*_*geb 6

尝试一次回答一个问题:

为什么要有__iter__方法?如果一个对象是一个迭代器,那么拥有一个返回自身的方法是没有意义的。

这不是毫无意义的。迭代器协议需要一个__iter____next__(或next在 Python 2 中)方法。我return self在他们的__iter__方法中见过的所有健全的迭代器,但拥有该方法仍然至关重要。没有它会导致各种怪异,例如:

somelist = [1, 2, 3]
it = iter(somelist)
Run Code Online (Sandbox Code Playgroud)

现在

iter(it)
Run Code Online (Sandbox Code Playgroud)

或者

for x in it: pass
Run Code Online (Sandbox Code Playgroud)

会抛出 aTypeError并抱怨it不可迭代,因为当iter(x)被调用时(当你使用for循环时会隐式发生)它期望参数对象x能够产生一个迭代器(它只是试图调用__iter__那个对象)。具体示例(Python 3):

>>> class A:
...     def __iter__(self):
...         return B()
...
>>> class B:
...     def __next__(self):
...         pass
...
>>> iter(iter(A()))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'B' object is not iterable
Run Code Online (Sandbox Code Playgroud)

考虑任何函数,特别是来自itertools 的期望可迭代的函数,例如dropwhile。使用任何具有__iter__方法的对象调用它都可以,无论它是不是迭代器的可迭代对象,还是迭代器——因为当iter使用该对象作为参数调用时,您可以期待相同的结果。在这里对两种可迭代对象进行奇怪的区分会违背 python 强烈拥护的鸭子类型原则。

整洁的技巧,如

>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(zip(*[iter(a)]*3))
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Run Code Online (Sandbox Code Playgroud)

如果您无法将迭代器传递给zip.

为什么要定义一些返回迭代器但本身不是迭代器的东西

让我们考虑这个简单的列表迭代器:

>>> class MyList(list):
...     def __iter__(self):
...         return MyListIterator(self)
>>>
>>> class MyListIterator:
...     def __init__(self, lst):
...         self._lst = lst
...         self.index = 0
...     def __iter__(self):
...         return self
...     def __next__(self):
...         try:
...             n = self._lst[self.index]
...             self.index += 1
...             return n
...         except IndexError:
...             raise StopIteration
>>>    
>>> a = MyList([1,2,3])
>>> for x in a:
...     for x in a:
...         x
...
1
2
3
1
2
3
1
2
3
Run Code Online (Sandbox Code Playgroud)

请记住,iter在两个for循环中都使用有问题的可迭代对象调用它,每次都期望从对象的方法中获得一个新的迭代器__iter__

现在,如果每次使用for循环时都没有生成迭代器,那么当一个MyList对象同时迭代任意次数时,您如何能够跟踪任何迭代的当前状态?哦,没错,你不能。:)

编辑:奖金和对 Tadhg McDonald-Jensen 评论的回复

可重用迭代器并非不可想象,但当然有点奇怪,因为它依赖于使用“不可消费”迭代器(即不是经典迭代器)进行初始化:

>>> class riter(object):
...     def __init__(self, iterable):
...         self.iterable = iterable
...         self.it = iter(iterable)
...     def __next__(self): # python 2: next
...         try:
...             return next(self.it)
...         except StopIteration:
...             self.it = iter(self.iterable)
...             raise
...     def __iter__(self):
...         return self
... 
>>> 
>>> a = [1, 2, 3]
>>> it = riter(a)
>>> for x in it:
...     x
... 
1
2
3
>>> for x in it:
...     x
... 
1
2
3
Run Code Online (Sandbox Code Playgroud)