1. 程式人生 > 其它 >迭代器,生成器,可迭代物件

迭代器,生成器,可迭代物件

迭代器,生成器

迭代器

一個類,如果實現:

  1. 實現 __iter__(self) 方法,並返回自身 self
  2. 實現 __next__(self) 方法,返回迭代的值。

舉例:

from collections.abc import Iterator,Iterable

class it(object):
    def __init__(self, num):
        self.num = num

    def __iter__(self):
        return self

    def __next__(self):
        self.num += 1
        # 大於10,丟擲異常
        if self.num > 10:
            raise StopIteration
        return self.num

ge = it(1)
print(isinstance(ge,Iterable))
print(isinstance(ge,Iterator))

可迭代物件

實現了 __iter__(self), 返回一個迭代器,就是一個可迭代物件。

iter() 可以將一個可迭代物件轉變成一個迭代器。

from collections.abc import Iterator,Iterable

class it(object):

    def __iter__(self):
        return iter([1,2,3])

ge = it(1)
print(isinstance(ge,Iterable))
print(isinstance(ge,Iterator))

生成器

使用 yield 關鍵字,可以實現生成器, 而且可以直接使用 next()

class generator(object):
    def __init__(self, num):
        self.num = num

    def __iter__(self):
        while self.num < 10:
            yield self.num
            self.num += 1

ge = generator(1)
print(type(ge))
print(isinstance(ge,Iterable))
print(isinstance(ge,Iterator))

當然,上面是一個生成器類,其實生成器跟 __iter__()

沒啥太大關係,只要隨便使用一個函式,使用 yield 關鍵字就行了:

def generator():
    yield 1
    
g = generator()
print(g)

生成器的 next(), send(), yield from

next(generator) 很簡單,就是不停的執行從當前程式碼,執行到下一個 yield 處,返回當前 yield 的資料

send(msg) 可以給生成器傳送資料,進行資料互動,然後執行到下一個 yield 處,返回 yield 的資料。如果沒有可以用來接收資料的變數,則和 next(generator) 效果一樣。生成器沒有啟動時,第一次使用 send() ,必須先發送一個 send(None) ,因為此時還沒有 yield 表示式可以來接收值。

yield from <generator> 它可以讓兩個生成器互動。可以從當前生成器,進入到另一個生成器執行,執行完畢後,再回到當前生成器。

def inner():
    print('enter inner')
    value = yield 2  # 第二次截止到: yield 2 ;  # 第三次從賦值開始: value = send傳遞過來的值
    print('inner value:',value)
    yield 3  # 第三次完畢
    return 'inner_return'  # 第四次開始

def outer():
    print('outer entered')
    yield 1   # 第一次截至到這裡

    value = yield from inner()  # 第二次起始:進入另一個生成器

    yield 4  # 第四次結束
    print('back to outer')  # 第五次開始
    print('value is :', value)  # 第五次結束,並丟擲 StopIteration 異常

o = outer()
print(next(o))  # 沒啥好說的,往下走
print(next(o))  # 沒啥好說的,往下走到 yield 2
print(next(o))  # 沒法傳送資料,所以 yield 2 前面的 value 會是 None
print(next(o))  # 沒啥好說的,不停的往下走
print(next(o))  # 沒啥好說的,不停的往下走

# ==================================================
o = outer()
print(o.send(None))  # o.send(None) 等同於 next(o),第一次必須傳送 None,先走到 yield 程式碼處
print(o.send('HH'))  # 儘管發了一個數據:HH,但是第1次 yield 1 前面沒有接收這個資料的變數,所以直接走向下一個 yield
print(o.send('HHH'))  # 上一次 yield 停止的地方:value = yield 2 有個變數可以接收值,所以給 value 賦值,然後走到下一個 yield 處
print(o.send('HHHH'))
print(o.send('HHHHH'))

for 迴圈

for 迴圈,可以迴圈迭代器,生成器,可迭代物件。它的原理,就是先執行要迴圈的物件的__iter__() 方法,獲得一個迭代器,然後執行迭代器的 __next__() 方法,並且在迴圈完畢時,處理一下 StopIteration 異常。雖然我們寫函式式的生成器時沒有__iter__() 方法,但是它已經內建了這個方法。

譬如:

def it():
    yield 1

ge = it()
print(dir(ge))
i = ge.__iter__()
print(dir(i))
print(next(i))

class it(object):
    def __iter__(self):
        return iter([1,2,3])

# ------- for --------
for i in it():
    print(i)

# ------- 原理 --------
i = it().__iter__()
while True:
    try:
        print(next(i))
    except StopIteration:
        break