为什么要有__iter__方法?如果一个对象是一个迭代器,那么拥有一个返回自身的方法是没有意义的。如果它不是一个迭代器而是一个可迭代的,即带有__iter__and__getitem__方法的东西,那么为什么要定义一些返回迭代器但不是迭代器本身的东西呢?在 Python 中,何时需要定义一个本身不是迭代器的可迭代对象?或者,什么是可迭代但不是迭代器的示例?
尝试一次回答一个问题:
为什么要有
__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)
| 归档时间: |
|
| 查看次数: |
2003 次 |
| 最近记录: |