[英]Why have an __iter__ method in Python?
為什么有一個__iter__
方法? 如果一個對象是一個迭代器,那么擁有一個返回自身的方法是沒有意義的。 如果它不是迭代器而是一個迭代器,即具有__iter__
和__getitem__
方法的東西,那么為什么要定義返回迭代器但不是迭代器本身的東西呢? 在 Python 中,何時需要定義一個本身不是迭代器的可迭代對象? 或者,什么是可迭代但不是迭代器的示例?
嘗試一次回答一個問題:
為什么有一個
__iter__
方法? 如果一個對象是一個迭代器,那么擁有一個返回自身的方法是沒有意義的。
這不是毫無意義的。 迭代器協議需要__iter__
和__next__
(或 Python 2 中的next
)方法。 我見過的所有理智的迭代器都只是在他們的__iter__
方法中return self
,但擁有該方法仍然至關重要。 沒有它會導致各種怪異,例如:
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__
方法的任何對象調用它都可以,無論它是不是迭代器的可迭代對象,還是迭代器——因為當使用該對象作為參數調用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)]
如果您無法將迭代器傳遞給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
請記住,對於兩個for
循環, iter
都是使用有問題的可迭代對象調用的,每次都期望從對象的__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
迭代器是可以迭代(循環)的東西,而迭代器是被消費的東西。
什么是可迭代但不是迭代器的示例?
簡單,一個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 的評論,我想如果您將項目添加到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
)
你在錯誤的方向思考。 迭代器必須實現__iter__
的原因是這樣,容器和迭代器都可以在for
和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__'
這也是為什么你只需要在迭代器的幾乎所有實現中都返回self
。 它並不意味着任何時髦的東西,只是語法上的一點點簡單。
參考: https : //docs.python.org/dev/library/stdtypes.html#iterator-types
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.