为什么要使用__iter__
方法?如果一个对象是一个迭代器,那么拥有一个返回自身的方法是没有意义的。如果它不是一个迭代器而是一个可迭代的,即带有__iter__
和__getitem__
方法的东西,那么为什么人们想要定义返回迭代器但不是迭代器本身的东西呢?在Python中,何时想要定义一个本身不是迭代器的迭代?或者,什么是可迭代但不是迭代器的东西的例子?
答案 0 :(得分:4)
尝试一次回答一个问题:
为什么要使用
__iter__
方法?如果一个对象是迭代器,那么有一个返回自身的方法是没有意义的。
这不是没有意义的。迭代器协议需要__iter__
和__next__
(或Python 2中的next
)方法。我在其return self
方法中看到的所有理智迭代器只有__iter__
,但使用该方法仍然至关重要。没有它会导致各种奇怪,例如:
somelist = [1, 2, 3]
it = iter(somelist)
现在
iter(it)
或
for x in it: pass
会抛出TypeError
并抱怨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
考虑任何函数,特别是来自期望可迭代的itertools的函数,例如dropwhile。使用具有__iter__
方法的任何对象调用它都没关系,无论它是不是迭代器的迭代器还是迭代器 - 因为调用{{1时可以得到相同的结果将该对象作为参数。在这里对两种迭代进行奇怪的区分将违背python强烈拥抱的duck typing原则。
像
这样的巧妙技巧iter
如果你无法将迭代器传递给>>> 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)]
,就会停止工作。
为什么人们想要定义返回迭代器但不是迭代器本身的东西
让我们考虑一下这个简单的列表迭代器:
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
调用iter
循环中的可迭代问题,每次从对象{{1}开始期待新迭代器方法。
现在,每次使用for
循环时都不会生成迭代器,当__iter__
对象迭代到任意时,您将如何跟踪任何迭代的当前状态同时多少次?哦,没错,你不能。 :)
编辑:对Tadhg McDonald-Jensen评论的回复和奖励
可恢复的迭代器并不是不可想象的,但当然有点奇怪,因为它依赖于使用&#34;非消耗品&#34;可迭代的(即不是经典的迭代器):
for
答案 1 :(得分:1)
可迭代是可以迭代(循环)的东西,其中迭代器是消耗的东西。
什么是可迭代但不是迭代器的东西的例子?
简单,list
。或者任何序列,因为您可以根据需要多次遍历列表而不会破坏列表:
>>> a = [1,2,3]
>>> for i in a:
print(i,end=" ")
1 2 3
>>> for i in a:
print(i,end=" ")
1 2 3
迭代器(如生成器)只能使用一次:
>>> b = (i for i in range(3))
>>> for i in b:
print(i,end=" ")
0 1 2
>>> for i in b:
print(i,end=" ")
>>> #iterator has already been used up, nothing gets printed
对于像迭代器一样使用的列表,您需要使用self.pop(0)
之类的东西来删除列表中的第一个元素进行迭代:
class IteratorList(list):
def __iter__(self):
return self #since the current mechanics require this
def __next__(self):
try:
return self.pop(0)
except IndexError: #we need to raise the expected kind of error
raise StopIteration
next = __next__ #for compatibility with python 2
a = IteratorList([1,2,3,4,5])
for i in a:
print(i)
if i==3: # lets stop at three and
break # see what the list is after
print(a)
给出了这个输出:
1
2
3
[4, 5]
你知道吗?这是迭代器的作用,一旦从__next__
返回一个值,它就没有理由在迭代器或内存中挂起,所以它被删除了。这就是为什么我们需要__iter__
来定义迭代器,让我们迭代序列而不会在过程中销毁它们。
在回复@timgeb's comment时,我想如果您将项目添加到IteratorList
,然后再次对其进行迭代,那将是有意义的:
a = IteratorList([1,2,3,4,5])
for i in a:
print(i)
a.extend([6,7,8,9])
for i in a:
print(i)
但是所有迭代器只对消耗或永不结束才有意义。 (如itertools.repeat
)
答案 2 :(得分:0)
您的思维方向错误。迭代器必须实现__iter__
的原因是,这样,容器和迭代器都可以在for
和{{1}中使用}。
in
这也是为什么您几乎需要在迭代器的几乎所有实现中返回> # list is a container
> list = [1,2,3]
> dir(list)
[...,
'__iter__',
'__getitem__',
...]
> # let's get its iterator
> it = iter(list)
> dir(it)
[...,
'__iter__',
'__next__',
...]
> # you can use the container directly:
> for i in list:
> print(i)
1
2
3
> # you can also use the iterator directly:
> for i in it:
> print(i)
1
2
3
> # the above will fail if it does not implement '__iter__'
的原因。它并不意味着任何时髦,只是语法上的一点简单。
参考:here