python的迭代器為什麼一定要實現__iter__方法?

__iter()__函數只有一句話,就是return self

查了很多資料,但他們關於這句話的解釋就是「返回迭代器本身」

但我想,return self即是返回迭代器對象本身的實例而已,那麼python解釋器不能自己處理這個實例么?為什麼一定要我們自己顯示返回給它呢?例如實現一個斐波拉契數列迭代器:

class Fib(object):

def __init__(self):

self.a = 0

self.b = 1

def next(self):

self.a , self.b = self.b , self.a + self.b

return self.a

def __iter__(self):

return self

調用時這樣:for i in Fib():print i

即是循環Fib()這個實例的屬性a而已,只要實現了next方法就行了,為什麼一定要規定__iter__返回實例自身呢?

初學python,在看迭代器的時候有些不懂,求指教。

補充:後來看了一些高級點的書,發現原來iter不僅僅是返回自身實例,也可以返回其他可迭代對象的實例,這樣就實現了委託迭代。所以對於for語句,我是這樣理解的:首先調用iter獲取需要迭代的對象,然後調用該對象的next方法,不知道理解對不對?如果是這樣的話,假設迭代器就是類本身的話,那事實上iter可有可無,只要實現next就可以了。


這是個和多態有關的問題,Python中關於迭代有兩個概念,第一個是Iterable,第二個是Iterator,協議規定Iterable的__iter__方法會返回一個Iterator, Iterator的__next__方法(Python 2里是next)會返回下一個迭代對象,如果迭代結束則拋出StopIteration異常。

同時,Iterator自己也是一種Iterable,所以也需要實現Iterable的介面,也就是__iter__,這樣在for當中兩者都可以使用。Iterator的__iter__只需要返回自己就行了。這樣,下面的代碼就可以工作:

for i in my_list:
...

for i in iter(mylist):
...

for i in (v for v in mylist if v is not None):
...

Python中許多方法直接返回iterator,比如itertools裡面的izip等方法,如果Iterator自己不是Iterable的話,就很不方便,需要先返回一個Iterable對象,再讓Iterable返回Iterator。生成器表達式也是一個iterator,顯然對於生成器表達式直接使用for是非常重要的。

那麼為什麼不只保留Iterator的介面而還需要設計Iterable呢?許多對象比如list、dict,是可以重複遍歷的,甚至可以同時並發地進行遍歷,通過__iter__每次返回一個獨立的迭代器,就可以保證不同的迭代過程不會互相影響。而生成器表達式之類的結果往往是一次性的,不可以重複遍歷,所以直接返回一個Iterator就好。讓Iterator也實現Iterable的兼容就可以很靈活地選擇返回哪一種。

總結來說Iterator實現的__iter__是為了兼容Iterable的介面,從而讓Iterator成為Iterable的一種實現。

補充一下題主對於for的理解基本上是正確的,但仍然有一點點偏差:for為了兼容性其實有兩種機制,如果對象有__iter__會使用迭代器,但是如果對象沒有__iter__,但是實現了__getitem__,會改用下標迭代的方式。我們可以試一下:

&>&>&> class NotIterable(object):
... def __init__(self, baselist):
... self._baselist = baselist
... def __getitem__(self, index):
... return self._baselist[index]
...
&>&>&> t = NotIterable([1,2,3])
&>&>&> for i in t:
... print i
...
1
2
3
&>&>&> iter(t)
&

當for發現沒有__iter__但是有__getitem__的時候,會從0開始依次讀取相應的下標,直到發生IndexError為止,這是一種舊的迭代協議。iter方法也會處理這種情況,在不存在__iter__的時候,返回一個下標迭代的iterator對象來代替。一個重要的例子是str,字元串就是沒有__iter__介面的。


__iter__

使類實例 Iterable

next()

使類實例 是 迭代器對象

註:『迭代器對象』跟『Iterable』是兩個東東喲。

題主提到,for語句來循環,它實際的工作流程是:

先判斷被循環的是否是Iterable,如果不是,儘管你實現了next(),它扔不會去調用,會直接報異常

所以,題主提到的

我是這樣理解的:首先調用iter獲取需要迭代的對象,然後調用該對象的next方法,不知道理解對不對

是對的。只不過,python解釋器是嚴格遵守『先調用iter來獲取迭代對象,然後再調用對象next』這個步驟來的,所以,你也必須實現__iter__。

一點拙見


這個問題,其實問的比較深了。

我分兩部分來說清楚。第一部分,先把一些概念說清楚。見後面。我這裡先說第二部分,回答正題。

====================

第二部分:

註:迭代器,為了和英文一致 我下面用 &<迭代器對象 &> 來取代。 另外&<可迭代對象&> 是容器一樣的對象,要區別開。

一個象容器一樣的對象,如果想要可以迭代,就需要實現 __iter__()這個方法。用這個方法,來返回一個 迭代器對象,這樣的話,就可以被for in這樣的語句來處理,象處理序列一樣來處理。

那麼定義中的 迭代器對象 要實現兩個方法 __iter__()和next() (在python3中改成了 __next__()了)方法。

題主的問題是:迭代器對象 的本職工作是來做 next 的,為什麼還要實現 __iter__(),返回它自己呢?

從語義上來說,是只需實現__next__()方法即可。但是, 迭代器對象實現了_iter__(),並返回它自己,就可以把它自己變成可迭代對象,那麼 迭代器對象 就可以 象 可迭代對象 一樣,用for in這種方法來 處理了。

例如:

m = iter(obj)
for n in m:
#do something.

這裡的 m 就是由obj返回的一個迭代器對象,它由於實現了 _iter__() :return self , 所以變成了和obj一樣的 可迭代對象,於是可以用for in來操作了。

在python官方文檔 PEP 234 -- Iterators 中,有提到

Classes can define how they are iterated over by defining an __iter__() method; this should take no additional arguments and return a valid iterator object. A class that wants to be an iterator should implement two methods: a next() method that behaves as described above, and an __iter__()method that returns self.

The two methods correspond to two distinct protocols:

  1. An object can be iterated over with for if it implements __iter__() or __getitem__().
  2. An object can function as an iterator if it implements next().

Container-like objects usually support protocol 1. Iterators are currently required to support both protocols. The semantics of iteration come only from protocol 2; protocol 1 is present to make iterators behave like sequences; in particular so that code receiving an iterator can use a for-loop over the iterator.

第一部分:

====================

分清了下面幾個概念,也就搞懂了python的迭代器:

1、 可迭代類(class collections.abc.Iterable)

提供 __iter__() 這個方法的類,都是可迭代類,

或者 提供__getitem __() 這個方法的類,也是可迭代類

2、迭代器類(class collections.abc.Iterator)

同時提供 __iter__() 和 __next__() 這兩個方法的類。

(從定義可以看出,迭代器類,一定是 可迭代類,因為它實現了__iter__()方法)

(從定義來看,迭代器類,要比可迭代類 多實現一個 __next()__方法。)

以上兩個,在這個頁面中可以找到:8.4. collections.abc - Abstract Base Classes for Containers - Python 3.6.3 documentation

以下兩個,在這個頁面中可以找到:

Glossary - Python 3.6.3 documentation

3、可迭代對象

簡單來說,就是那些 list, str, 和tuple 用這些定義的對象,都是 可迭代對象,因為他們都實現了__iter__() 或 __getitem__()方法。(當然,也可以是你自己定義一個類 生成的一個對象,下面例子中:用Fib()類所生成的對象)

4、迭代器對象

代表 數據流的對象。傳說中的迭代器。

你可以把 可迭代對象,當成一個容器(其實它就是一個容器 英文官方叫collections)。那麼,如果你要從這個容器中,一個一個把裡面的數據取出來,那麼你可以 造作一個 迭代器類(例子中有kkk()),用它生成的迭代器對象(例子中的「K」,可以返回給iter()調用者 ),可以幫你一個一個從容器里取數據。所以,迭代器必須實現—__next__()方法。(注意python3以後,只能叫__next__()方法,這個方法不能帶參數)

因為 迭代器類 也實現了__iter__()方法,所以它當然也是一個 可迭代類。

迭代器對象是如何得到的呢,很簡單, d = iter(kkk) 就可以把 kkk 的迭代器對象(「K」)取出來了。

(注意:1、 這裡的iter()函數,不同於__iter__();2、kkk 必須是一個可迭代對象,比如是 kkk = [1,3,5,4,3,100]這樣的,或是kkk內實現了 __iter__()方法。)

一般來說,當你自己定義一個可迭代類時,我們希望用 一個 迭代器對象 來取 它自己的數據,所以__iter__()只需要返回自己即可,即return self。

總結一下: 分清兩個類,兩個對象。 一個對話:

##### 主程序說:

既然你是一個可迭代對象,那請把 你的迭代器(迭代器對象),給我玩一玩啊。

##### 可迭代對象:

好啊,你來拿吧。

##### 主程序說:

怎麼拿啊?

##### 可迭代對象:

你 iter()一下我啊!

##### 主程序說:

OK,拿到。

##### 主程序說:

好好玩啊,你的這個迭代器,晃一下,出來一個東西,晃一下又出來一個。

##### 可迭代對象:

那是當然,不過,我的這個迭代器,不能老晃,不然晃完了,可就沒有了哦。隔壁老王家也有一個迭代器,它那個隨便你,想晃多少晃多少。

對話結束。

最後,大家網路上所說的迭代器, 一般是指 迭代器對象, 即英文材料中所指的 iterator。

```

$ cat fib.py
from collections import Iterable

class kkk(object):
def __init__(self):
self.a = 1
def __iter__(self):
return self
def __next__(self):
self.a = self.a + 1
if self.a &> 20: # 退出循環的條件
raise StopIteration();
return self.a
def next(self):
return self.a + 1000

class Fib(object):
#class Fib(Iterable):
def __init__(self,m,n):
self.a, self.b = 0, 1 # 初始化兩個計數器a,b
self.m = m
self.n = n
self.K = kkk() #kkk也是一個可迭代類,同時,也是一個迭代器類

#根據需要,你可以返回不同的迭代器對象。
def __iter__(self):
if self.m ==5:
return self.K
else:
return self
#return self # 實例本身就是迭代對象,故返回自己

def next(self): # next()在python3中,已經不具有其它含義,只是普通的一個方法。
return self.a + 5555

def addd(self):
self.a, self.b = self.b, self.a + self.b # 計算下一個值
if self.a &> 100: # 退出循環的條件
raise StopIteration();
return self.a # 返回下一個值

def __next__(self):
self.a, self.b = self.b, self.a + self.b # 計算下一個值
if self.a &> 100: # 退出循環的條件
raise StopIteration();
return self.a # 返回下一個值

if __name__ == "__main__":
x = Fib(5,6)
print(x.__next__())
print(x.__next__())
print(x.__next__())
print(x.__next__())

print(x.next())
print(x.next())
print(x.next())
print(x.next())

print( "---------------------------------")
print(x.addd())
print(x.addd())
print(x.addd())
print(x.addd())
print( "------in Fib(5,4)---------------------------")
for n in Fib(5,4):
print (n)
print( "------in Fib(3,4)---------------------------")
for n in Fib(3,4):
print (n)

print( "-------------in m --5,4------------------")
m = Fib(5,4)
for n in m:
print(n)

print( "-------------in m --3,4------------------")
m = Fib(4,4)
"""
print("-----m.next() is:",m.next() )
print("-----m.next() is:",m.next() )
print("-----m.next() is:",m.next() )
"""
for n in m:
print(n)

print( "-------------in iter(m) --5,4------------------")
m = Fib(5,4)
x = iter(m) #請問,iter是誰的內置函數
print("-----iter(m).next() is:",x.next() )
print("-----iter(m).next() is:",x.next() )
print("-----iter(m).next() is:",x.next() )

for n in x:
print(n)

print( "-------------in iter(m) --3,4------------------")
m = Fib(3,4)
x = iter(m)
print("-----iter(m).next() is:",x.next() )
print("-----iter(m).next() is:",x.next() )
print("-----iter(m).next() is:",x.next() )

print("-----iter(m).next() is:",x.__next__() )
print("-----iter(m).next() is:",x.__next__() )
print("-----iter(m).next() is:",x.__next__() )
print("-----iter(m).next() is:",x.__next__() )
print("-----iter(m).next() is:",x.__next__() )
for n in x:
print(n)

print( "-----------------in iter(m) again---------------")

y = iter(m)
for n in y:
print(n)

print( "--------------end-------------------")
#Fib.__next__()

L = [1,3,2,5,6,7,9,6]
for n in iter(L):
print("L:",n)

for n in iter(L):
print("L:",n)

print(dir("__builtins__"))
print(dir("__class__"))

$

```

運行結果:

```

$ python fib.py
1
1
2
3
5558
5558
5558
5558
---------------------------------
5
8
13
21
------in Fib(5,4)---------------------------
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
------in Fib(3,4)---------------------------
1
1
2
3
5
8
13
21
34
55
89
-------------in m --5,4------------------
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-------------in m --3,4------------------
1
1
2
3
5
8
13
21
34
55
89
-------------in iter(m) --5,4------------------
-----iter(m).next() is: 1001
-----iter(m).next() is: 1001
-----iter(m).next() is: 1001
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-------------in iter(m) --3,4------------------
-----iter(m).next() is: 5555
-----iter(m).next() is: 5555
-----iter(m).next() is: 5555
-----iter(m).next() is: 1
-----iter(m).next() is: 1
-----iter(m).next() is: 2
-----iter(m).next() is: 3
-----iter(m).next() is: 5
8
13
21
34
55
89
-----------------in iter(m) again---------------
--------------end-------------------
L: 1
L: 3
L: 2
L: 5
L: 6
L: 7
L: 9
L: 6
L: 1
L: 3
L: 2
L: 5
L: 6
L: 7
L: 9
L: 6

====================


因為有了__iter__就可以用for in 這個語法糖。你把遍歷iterator和iterable 的代碼分別寫出來,看看哪個省事。

在iterator不是iterable 的語言中,你拿到了一個迭代器,又不能用for in遍歷,怎麼辦??包一層。

class WrpperIterable& implements Iterable& {

...

private Iterator& iter;

...

@overwrite

public Iterator& iterator() { return iter; }

}


沒問題,可迭代對象通過實現__iter__()返回迭代器,迭代器中實現next方法就可以了,不一定要實現Iter,只不過是大多數的迭代器返回自身而已。


推薦閱讀:

同時裝了Python3和Python2,怎麼用pip?
Python3中如何得到Unicode碼對應的中文?

TAG:Python | 編程 | Python3x | Python框架 | Python入門 |