1. 程式人生 > >用程式碼來解釋可迭代性,迭代器,生成器的區別

用程式碼來解釋可迭代性,迭代器,生成器的區別

一. 創造器(creator)

這是我自己造的一個名詞,因為在python術語中,對只實現了__next__()方法的物件,好像沒有任何名分,為了說明,我將只實現了__next__()方法的物件稱為創造器(creator)。

class O_Next:
    def __init__(self):
        self.index = -1
        
    def __next__(self):
        self.index += 1
        return self.index

o_next = O_Next()

import collections
print(isinstance(o_next, collections.Iterable))
print(isinstance(o_next, collections.Iterator))


>>> False
>>> False

通過Python 的型別檢查,可以看到,創造器即沒有可迭代性,又不是python認可的迭代器,是一個無名英雄,還好我給它起了個名字來識別,創造器。

用標準函式 next(), 每呼叫一次創造器物件,將執行一次物件內部的__next__()方法;外部呼叫程式可決定需要呼叫多少次。

result = [next(o_next) for i in range(5)]
print(result)

result = [next(o_next) for i in range(5)]
print(result)

>>> [0, 1, 2, 3, 4]
>>> [5, 6, 7, 8, 9]

若用 while True 進行呼叫,物件將會被無限迴圈地呼叫下去;可在物件內部增加停止迭代的條件判斷,通過觸發 StopIteration異常,讓呼叫程式捕獲後,來終止迭代處理。

class O_Next2():
    def __init__(self):
        self.index = -1
        
    def __next__(self):
        if self.index > 5 - 1:
            raise StopIteration
        else:
            self.index += 1
            return self.index

o_next2 = O_Next2()
while True:
    try:
        print(next(o_next2))
    except StopIteration as e:
        break  


>>> 0
>>> 1
>>> 2
>>> 3
>>> 4
>>> 5

 

二. 可迭代物件(Iterable)

具有可迭代性的物件是內部只需要實現了 __iter__()方法的物件,該方法必須返回一個實現了 __next__()方法的創造器物件(官方認為必須返回的是迭代器物件,其實不然),否則無法被外部程式用for, list()等方法正常呼叫。比如: list(), set(), tuple()等,我們可以看看常見容器型別物件的可迭代性,是否為迭代器,及__next__()返回的迭代器物件

import collections

print(isinstance(list(), collections.Iterable), isinstance(list(), collections.Iterator), type(iter(list())))

print(isinstance(set(), collections.Iterable), isinstance(set(), collections.Iterator), type(iter(set())))

print(isinstance(dict(), collections.Iterable), isinstance(dict(), collections.Iterator), type(iter(dict())))

print(isinstance(tuple(), collections.Iterable), isinstance(tuple(), collections.Iterator), type(iter(tuple())))

>>> True False <class 'list_iterator'>
>>> True False <class 'set_iterator'>
>>> True False <class 'dict_keyiterator'>
>>> True False <class 'tuple_iterator'>

可見,常見容器物件具有可迭代性,但不是迭代器,它們有各自專用的迭代器。

接下來,我們自定義一個可迭代的物件,用自定義的創造器(不是迭代器)。

class O_Iter():
    def __iter__(self):
        return O_Next2() 
o_iter = O_Iter()

print(isinstance(o_iter, collections.Iterable), isinstance(o_iter, collections.Iterator),  type(iter(o_iter)))

>>> True False <class '__main__.O_Next2'>

用for, list(),呼叫可迭代物件,將返回一個創造器物件例項, 然後無限迴圈呼叫該創造器物件的 __next__()方法,並自動處理 StopIteration 異常以終止迭代處理。

x = [i for i in o_iter]
print(x)

print(list(o_iter))

>>> [0, 1, 2, 3, 4, 5]
>>> [0, 1, 2, 3, 4, 5]

 

三. 迭代器(Iterator)

迭代器(Iterator)是內部同時實現了__iter__()方法和__next__()方法的物件。

__next__()方法實現創造器邏輯,__iter__()方法提供可迭代性,並返回物件自身,或其它創造器。

在python的世界裡,認為實現了__next__()方法的物件,基本都會同時實現__iter__()方法,返回自身,變成迭代器。因此,沒有了只實現__next__()方法的創造器的位置。

class O_Iterator():
    def __init__(self):
        self.index = -1
        
    def __next__(self):
        if self.index > 5 - 1:
            raise StopIteration
        else:
            self.index += 1
            return self.index
        
    def __iter__(self):
        return O_Iterator()
    
o_iterator = O_Iterator()

print(isinstance(o_iterator, collections.Iterable), isinstance(o_iterator, collections.Iterator),  type(iter(o_iterator)))


>>> True True <class '__main__.O_Iterator'>

迭代器本身也具有可迭代性

x = [i for i in o_iterator]
print(x)

print(list(o_iterator))

>>> [0, 1, 2, 3, 4, 5]
>>> [0, 1, 2, 3, 4, 5]

 

四. 生成器(generator)

用更優雅簡潔的方式來同時實現__iter__(), __next__(), 並自動觸發StopIteration 異常

1. 生成器函式

生成器函式的例項呼叫,具有可迭代性,而且是迭代器

import collections

def o_generator(n):
    index = 0
    
    while index < n:
        yield index
        index += 1

print(isinstance(o_generator(5), collections.Iterable), isinstance(o_generator(5), collections.Iterator),  type(iter(o_generator(5))))

>>> True True <class 'generator'>
x = [i for i in o_generator(5)]
print(x)

print(list(o_generator(5)))

>>> [0, 1, 2, 3, 4]
>>> [0, 1, 2, 3, 4]

2. 生成器表示式

列表生成器不加方括[ ],而是用圓括號( ),就變成了一個生成器物件(generator)

o_gen2 = (i*i for i in range(5))

print(isinstance(o_gen2, collections.Iterable), isinstance(o_gen2, collections.Iterator),  type(iter(o_gen2)))

>>> True True <class 'generator'>

可迭代處理

o_gen2 = (i*i for i in range(5))

x = [i for i in o_gen2]
print(x)

print(list(o_gen2))

>>> [0, 1, 4, 9, 16]
>>> []

為什麼第2次 print()輸出的為空???

對同一個迭代器物件例項,全部遍歷完後,就結束了,不會自己從頭再來。 仔細分析觀察其他的程式碼,每次都是重新生成了一個迭代器物件例項(創造器物件例項),初始化後,從頭開始遍歷的。我們可以把生成器函式的例子改寫一下看看,結果將一樣。

def o_generator(n):
    index = 0
    
    while index < n:
        yield index
        index += 1


o_gen = o_generator(5)

x = [i for i in o_gen]
print(x)

print(list(o_gen))


>>> [0, 1, 2, 3, 4]
>>> []