__ iter__函式和__next__函式

迭代器就是重複地做一些事情,可以簡單的理解為迴圈,在python中實現了__iter__方法的物件是可迭代的,實現了next()方法的物件是迭代器,這樣說起來有點拗口,實際上要想讓一個迭代器工作,至少要實現__iter__方法和next方法。很多時候使用迭代器完成的工作使用列表也可以完成,但是如果有很多值列表就會佔用太多的記憶體,而且使用迭代器也讓我們的程式更加通用、優雅、pythonic。

如果一個類想被用於for 。。。 in迴圈,類似list或tuple那樣,就必須實現一個__iter__()方法,該方法返回一個迭代物件,然後,Python的for迴圈就會不斷呼叫該迭代物件的next()方法拿到迴圈的下一個值,直到遇到StopIteration錯誤時退出迴圈。

容器(container):

容器是用來儲存元素的一種資料結構,容器將所有資料儲存在記憶體中,Python中典型的容器有:list,set,dict,str等等。

class

test

():

def

__init__

self

data

=

1

):

self

data

=

data

def

__iter__

self

):

return

self

def

__next__

self

):

if

self

data

>

5

raise

StopIteration

else

self

data

+=

1

return

self

data

for

item

in

test

3

):

print

item

輸出結果:

4

5

6

for … in… 這個語句其實做了兩件事。第一件事是獲得一個可迭代器,即呼叫了__iter__()函式。 第二件事是迴圈的過程,迴圈呼叫__next__()函式。

對於test這個類來說,它定義了__iter__和__next__函式,所以是一個可迭代的類,也可以說是一個可迭代的物件(Python中一切皆物件)。

迭代器:

含有__next__()函式的物件都是一個迭代器,所以test也可以說是一個迭代器。如果去掉__itet__()函式,test這個類也不會報錯。如下程式碼所示:

class

test

():

def

__init__

self

data

=

1

):

self

data

=

data

def

__next__

self

):

if

self

data

>

5

raise

StopIteration

else

self

data

+=

1

return

self

data

t

=

test

3

for

i

in

range

3

):

print

t

__next__

())

輸出結果:

4

5

6

生成器:

生成器是一種特殊的迭代器。當呼叫fib()函式時,生成器例項化並返回,這時並不會執行任何程式碼,生成器處於空閒狀態,注意這裡prev, curr = 0, 1並未執行。然後這個生成器被包含在list()中,list會根據傳進來的引數生成一個列表,所以它對fib()物件(一切皆物件,函式也是物件)呼叫__next__方法。

def

fib

end

=

1000

):

prev

curr

=

0

1

while

curr

<

end

yield

curr

prev

curr

=

curr

curr

+

prev

print

list

fib

()))

輸出結果:

1

1

2

3

5

8

13

21

34

55

89

144

233

377

610

987

上面只是做了幾個演示,這裡具體說明一下:

當呼叫iter函式的時候,生成了一個迭代物件,要求__iter__必須返回一個實現了__next__的物件,我們就可以透過next函式訪問這個物件的下一個元素了,並且在你不想繼續有迭代的情況下丟擲一個StopIteration的異常(for語句會捕獲這個異常,並且自動結束for),下面實現了一個自己的類似range函式的功能。

class

MyRange

object

):

def

__init__

self

end

):

self

start

=

0

self

end

=

end

def

__iter__

self

):

return

self

def

__next__

self

):

if

self

start

<

self

end

ret

=

self

start

self

start

+=

1

return

ret

else

raise

StopIteration

from

collections。abc

import

*

a

=

MyRange

5

print

isinstance

a

Iterable

))

print

isinstance

a

Iterator

))

for

i

in

a

print

i

輸出結果是:

True

True

0

1

2

3

4

接下來我們使用

next

函式模擬一次:

class

MyRange

object

):

def

__init__

self

end

):

self

start

=

0

self

end

=

end

def

__iter__

self

):

return

self

def

__next__

self

):

if

self

start

<

self

end

ret

=

self

start

self

start

+=

1

return

ret

else

raise

StopIteration

a

=

MyRange

5

print

next

a

))

print

next

a

))

print

next

a

))

print

next

a

))

print

next

a

))

print

next

a

))

# 其實到這裡已經完成了,我們在執行一次檢視異常

可以看見一個很明顯的好處是,每次產生的資料,是產生一個用一個,什麼意思呢,比如我要遍歷

[0, 1, 2, 3。。。。。]

一直到10億,如果使用列表的方式,那麼是會全部載入記憶體的,但是如果使用迭代器,可以看見,當用到了(也就是在呼叫了

next

)才會產生對應的數字,這樣就可以節約記憶體了,這是一種懶惰的載入方式。

總結

可以使用

collection。abs

裡面的

Iterator

Iterable

配合

isinstance

函式來判斷一個物件是否是可迭代的,是否是迭代器物件。

iter

實際是對映到了

__iter__

函式。

只要實現了

__iter__

的物件就是可迭代物件(

Iterable

),正常情況下,應該返回一個實現了

__next__

的物件(雖然這個要求不強制),如果自己實現了

__next__

,當然也可以返回自己。

同時實現了

__iter__

__next__

的是迭代器(

Iterator

),當然也是一個可迭代物件了,其中

__next__

應該在迭代完成後,丟擲一個

StopIteration

異常。

for

語句會自動處理這個

StopIteration

異常以便結束

for

迴圈。