1. 程式人生 > >徹底理解Python迭代器

徹底理解Python迭代器

如何理解迭代物件迭代器以及迭代協議:

 Iterable              Iterator

1、可迭代物件和迭代器

可迭代的物件有個 __iter__ 方法,每次都例項化一個新的迭代器;而迭代器要實現 __next__ 方法,返回單個元素,此外還要實現 __iter__ 方法,返回迭代器本身

可迭代的物件一定不能是自身的迭代器。也就是說,可迭代的物件必須實現

__iter__ 方法,但不能實現 __next__ 方法

2、迭代器:

迭代器是這樣的物件:實現了無引數的 __next__ 方法,返回序列中的下一個元素;如果沒有元素了,那麼丟擲 StopIteration 異常。 Python 中的迭代器還實現了

__iter__ 方法,因此迭代器可以迭代。

3、序列可以迭代原因

    iter 函式:直譯器需要迭代物件X時,會自動呼叫iter(X)

4內建iter函式作用

1、檢查物件是否實現了__iter__方法,如果實現就呼叫,獲取一個迭代器。

2、如果沒有實現__iter__,但是有__getitem__方法,Python會建立一個迭代器,嘗試按順序從索引0開始獲取元素。

3、如果嘗試失敗丟擲TypeError異常

4、理解迭代協議

Python 迭代協議要求一個 __iter__() 方法返回一個特殊的迭代器,這個迭代器實現了 __next__() 方法,並通過StopIteration

異常標識迭代完成。

Eg: 如下程式碼,按照迭代協議描述:

# -*- coding:utf -*-

import re

import reprlib

RE_WORD= re.compile('\w+')

class Sentence:

    def__init__(self, text):

        self.text= text

        self.words= RE_WORD.findall(text)

    def__repr__(self):

        return 'Sentence(%s)' %reprlib.repr(self.text)

    def__iter__(self):

        return SentenceIterator(self.words)

class SentenceIterator:

    def__init__(self, words):

        self.words= words

        self.index= 0

def __next__(self):

        try:

            word = self.words[self.index]

        except IndexError:

            raise StopIteration()

        self.index+= 1

return word

    def__iter__(self):

        return self

if __name__ == '__main__':

    s = Sentence('helloworld')

    it = iter(s)

print(it) 

    print(next(it))

    print(next(it)) 

結果:

<__main__.SentenceIteratorobject at 0x03561F30>

hello

World

符合可迭代物件和迭代器的描述情況。

如果我們不按照這種協議的說明:__iter__ 函式不返回一個迭代器,使用別的函式返回迭代器情況會是什麼。修改程式碼如下,並執行列印:

# -*- coding:utf -*-

import re

import reprlib

RE_WORD= re.compile('\w+')

class Sentence:

    def__init__(self, text):

        self.text= text

        self.words= RE_WORD.findall(text)

    def__repr__(self):

        return 'Sentence(%s)' %reprlib.repr(self.text)

    def__iter__(self):

        return iter(self.words)

    defshow_word(self):

        return SentenceIterator(self.words)

class SentenceIterator:

    def__init__(self, words):

        self.words= words

        self.index= 0

def __next__(self):

        try:

            word = self.words[self.index]

        except IndexError:

            raise StopIteration()

        self.index+= 1

return word

    def__iter__(self):

        return self

if __name__ == '__main__':

    s = Sentence('helloworld')

    print(s.show_word())

    print(next(s.show_word()))

    forword in s.show_word():

        print(word)

執行結果:

<__main__.SentenceIteratorobject at 0x03271250>

hello

hello

world

接著執行如下:

if __name__ == '__main__':

    s = Sentence('helloworld')

    it = iter(s)

    print(it)

    print(next(it))

    print(next(it))

執行結果:

<list_iteratorobject at 0x02B21FB0>

hello

world

因為 __iter__ 函式返回值是個list 型別,所以使用iter()函式依舊可以生成一個list_iterator。原始資料可迭代,使用iter()函式可生成一個迭代器

如果原始資料不可迭代,呼叫iter就會報錯:

>>> s = 14

>>> s

14

>>>iter(14)

Traceback (mostrecent call last):

  File "<pyshell#51>", line 1,in <module>

    iter(14)

TypeError: 'int'object is not iterable

以上兩種方式是為了理解迭代物件和迭代器,實時上這種寫法過於繁瑣,使用生成器來實現迭代器:

# -*- coding:utf -*-

import re

import reprlib

RE_WORD= re.compile('\w+')

class Sentence:

    def__init__(self, text):

        self.text= text

        self.words= RE_WORD.findall(text)

    def__repr__(self):

        return 'Sentence(%s)' %reprlib.repr(self.text)

    def__iter__(self):

        for i in self.words:

            yield i

if __name__ == '__main__':

    s = Sentence('helloworld')

    it = iter(s)

    print(it)

    print(next(it))

    print(next(it))

輸出結果:

<generator objectSentence.__iter__ at 0x0355DF60>

hello

world

使用生成器很大減少了繁瑣的自己實現一個迭代器並且實現 __next__方法。

當然生成器除了使用: yield關鍵字以外還可以使用生成器表示式,類似於列表推導式。

# -*- coding:utf-8 -*-

def gen_():

    print('start')

    yield'a'

print('continue')

    yield'b'

print('end.')

res1 =[x*3 for x in gen_()]

print(res1)

res2 =(x*3 for x in gen_())

print(res2)

輸出結果:

start

continue

end.

['aaa', 'bbb']

<generator object<genexpr> at 0x02B36540>

列表推導式直接打印出來了所有的結果,而生成器表示式並沒有,生成器表示式可以使用next()函式來輸出結果。