python: 可迭代對象、迭代器、生成器

一、迭代

1、定義

迭代是指通過for循環遍歷對象每一個元素的過程。

二、可迭代對象(iterable)

1、定義

可迭代對象是定義了 __iter__() 方法或 __getitem__() 方法的類對象。

2、__iter__()

from collections import abc


class MyIterable:
    def __iter__(self):
        pass


my_iterable = MyIterable()

3、__getitem__()

from collections import abc


class MyIterable:
    def __getitem__(self, item):
        pass


my_iterable = MyIterable()

定義了__iter__()__getitem__()方法後,對象就可以使用for循環遍歷了。當然,上面只是一個簡單的示例,什麼功能都沒有,實際情況一般基於Python的內置數據類型(int, list, string…)去寫。在引入迭代器之後,__iter__()的功能一般是返回一個迭代器。

4、說明

很多文章喜歡拿 collections.abc.Iterable 對象和一個類對象比較,然後判斷這個類對象是否是可迭代對象。如:

from collections import abc
class MyIterable:
    def __getitem__(self, item):
        pass
my_iterable = MyIterable()
print(isinstance(my_iterable, abc.Iterable))

其實這是不完全正確的。因爲根據Python文檔, Iterable 只實現了__iter__()方法,所以一個對象如果實現 了__getitem__()方法,但是沒有實現__iter__()方法,用這種方式判斷,該對象就會被判定爲不是可迭代對象,這是錯誤的。只要實現這兩個方法中的任何一個,都是可迭代對象,應該使用**iter()**方法。

三、迭代器

1、定義

實現了迭代器協議(__iter__(), __next__())的類對象稱爲迭代器。迭代器是在Python 2.2引入的,因爲迭代器實現了__iter__()方法,所以迭代器也屬於可迭代對象。

2、__iter__()

返回對象本身。

3、__next__()

返回對象的下一個元素,如果沒有下一個元素則拋出 StopIteration異常。

# 模仿range()迭代器自定義一個迭代器
class MyRangeIterator:
    def __init__(self, num):
        self.num = num
        self.index = 0  # 設置第一個元素

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < self.num:
            val = self.index
            self.index += 1  # 爲了每次返回下一個元素,所以每次+1
            return val
        else:
            raise StopIteration()  # 如果沒有元素了則拋出StopIteration


mri = MyRangeIterator(5)
print([i for i in mri])  # [0, 1, 2, 3, 4]
print([i for i in mri])  # []

4、典型的可迭代對象與迭代器

典型的可迭代對象的 __iter__()方法返一個新的迭代器實例,而迭代器的__iter__()方法返回迭代器本身。

# 可迭代對象
class MyRange:
    def __init__(self, num):
        self.num = num

    def __iter__(self):
        return MyRangeIterator(self.num)


# 迭代器
class MyRangeIterator:
    def __init__(self, num):
        self.num = num
        self.index = 0  # 設置第一個元素

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < self.num:
            val = self.index
            self.index += 1  # 爲了每次返回下一個元素,所以每次+1
            return val
        else:
            raise StopIteration()  # 如果沒有元素了則拋出StopIteration


mr = MyRange(5)
print([i for i in mr])  # [0, 1, 2, 3, 4]
print([i for i in mr])  # [0, 1, 2, 3, 4]

四、迭代原理

解釋器需要迭代對象 x 時,每迭代一次:

(1)執行 iter(x)。

(2)檢查對象是否實現了__iter__方法,如果實現了就調用它,獲取到一個迭代器iterator。

(3)獲取到迭代器之後,執行next(iterator)

(4)然後執行 iterator.__next__()獲取元素。

(5)如果沒有元素了就拋出 StopIteration

五、生成器

1、定義

根據Python術語表,有:

(1)generator

包含 yield 表達式的函數或者生成器叫做生成器。生成器本質上是一個函數,也叫生成器函數(generator function)。

示例:

def gen_123():
    yield 123


print(gen_123)  #<function gen_123 at 0x00000269E04D6558>,生成器本質上是一個函數

(2)generator iterator

生成器生成的對象(generator object,簡稱生成器對象)稱爲“generator iterator”。爲什麼這樣命名呢?因爲生成器對象也是一個迭代器。

示例:

from collections import abc


def gen_123():
    yield 123


print(gen_123)  # <function gen_123 at 0x00000269E04D6558>

g = gen_123()
print(g)  # <generator object gen_123 at 0x00000269E0446C48>,生成器對象
print(isinstance(g, abc.Iterator))  # True,生成器生成的對象也是一個迭代器

下面是個人理解(不一定正確):

a. generator iterator這個名稱有點“怪”,咋一看,你不知道它所表達的意思到底是生成器還是迭代器,還有點拗口,可能官方想表達的是生成器生成的迭代器

b.有人喜歡把生成器稱爲“特殊的迭代器”,其實這是不對的,生成器本質是函數,而迭代器本質是類對象。生成器生成的對象纔是迭代器。

2、yield

關於yield的實現原理,可以參考這篇文章Python yield。本文只說yield的用法。

yield表達式 的語法如下:

yield_atom       ::=  "(" yield_expression ")"
yield_expression ::=  "yield" [expression_list | "from" expression]

yield關鍵字的功能:

(1)返回給調用者一個值,並掛起當前狀態。

(2)當執行next(generator iterator),從離開的地方繼續。

3、示例

class MyRange:
    def __init__(self, num):
        self.num = num
        self.index = 0

    def __iter__(self):
        return self.my_range_generator()

    def my_range_generator(self):
        while self.index < self.num:
            yield self.index
            self.index += 1


my_range = MyRange(5)
for i in my_range:
    print(i, end=' ')  # 0 1 2 3 4 

4、生成器表達式

生成器表達式的作用和生成器函數的作用也是一樣的,生成一個generator iterator。所以也是一種生成器。

(1)語法

generator_expression ::=  "(" expression comp_for ")"

(2)示例

g = (x*y for x in range(10) for y in range(x, x+10))

參考資料

[1]PEP234, iterator: https://www.python.org/dev/peps/pep-0234/

[2]PEP255, Simple Generators: https://www.python.org/dev/peps/pep-0255/

[3]PEP342, Coroutines via Enhanced Generators:https://www.python.org/dev/peps/pep-0342/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章