1. 程式人生 > >python快速學習系列(7):迭代器

python快速學習系列(7):迭代器

迭代器協議
1.迭代器協議
·迭代器是一個物件
·迭代器可以被next()函式呼叫,並返回一個值
·迭代器可以被iter()函式呼叫,並返回迭代器自己
·連續被next()呼叫時返回一系列的值
·如果到了迭代的末尾,則丟擲StopIteration異常
·迭代器也可以沒有末尾,只要被next()呼叫,就一定會返回一個值
·python中,next()內建函式呼叫的是物件的__next__()方法
·python中,iter()內建函式呼叫的是__iter__()方法
·一個實現了迭代器協議的物件可以被for語句迴圈迭代直到終止

1)只要一個物件實現了__next__()方法,就可以被next()函式呼叫

class XIterator:
    def __next__(self):
        return 'hello world'
def main()
    x_it = XIterator()
    [print(next(x_it)) for i in range(3)]
#結果是列印hello world 3次

2)for語句的內部實現

for element in iterable:
    #do somthing with element

#create an iterator object from that iterable
iter_obj = iter(iterable)  #相當於從一個可迭代器裡返回一個可迭代物件
#infinite loop
while True:
    try:
        #get the next item 
        element = next(iter_obj)
        #do somthing with element  也就是說,我們平時寫for語句都是從這裡開始寫,之前和之後的都是直譯器完成的     
    except StopIteration:
        #if StopIteration is raised , break from loop
        break

說明:
for語句裡用的是iterable(可迭代物件),而非iterator(迭代器)
for語句執行的第一個操作是從哪一個iterable生成一個iterator
for語句的迴圈體其實是靠檢測StopIteration異常來中斷的
要想被for語句迭代需要三個條件:iter(),next(),StopIteration

3)一個標準的迭代器的寫法:有__init__,next,iter,StopIteration

class XIterator:
    def __init__(self):
        self.elements = list(range(5))
    def __next__(self):
        if self.elements:
            return self.elements.pop()
        else:
            raise StopIteration
    
    def __iter__(self):
        return self

def main()
    x_it = XIterator()
    for x in x_it:
        print(x)

2.Generator
·迭代器很有用,但是實現起來有些繁瑣,沒關係,生成器來幫你
·生成器在保持程式碼簡潔優雅的同時,自動實現了迭代器協議
1)實現生成器的方式一:yeild Expression

def f():
    yield 1
    yield 2
    yield 3
def main():
    f_gen = f()
    for x in f_gen:
        print(x)

關鍵字yield和return的區別:yield是暫停,稍後繼續,並會自動實現StopIteration;而return是直接終止

2)實現生成器方式二:Generator expression

 [print(x) for x in (x ** 2 for x in range(5))]
#後面()內的表示式就是生成器表示式,即list comprehension的[]變成()就可

不要小看任何看似微小的區別:

sum([x ** 2 for x in range(1000000)])
sum(x ** 2 for x in range(1000000))

第一行:先生成一個長度為1000000的列表,然後在進行就和–>記憶體爆炸
第二行:其實是一個生成器,只是由於sum本身具有()而省略了(),這裡的操作是聚合,即生成一個求和,生成一個求和(幾乎不佔記憶體)

也就是說,如果list存在的價值僅僅是迭代的話,用generator要好得多

3.為什麼需要生成器
·相比迭代器協議,實現生成器的程式碼量小,可讀性高
·相比在List中操作元素,直接使用生成器能節省大量記憶體
·有時候我們會需要寫出一個無法在記憶體中存放的無線資料流
·可以建立生成器管道(多個生成器鏈式呼叫)

1)用生成器表示全部的斐波那契數列

def fibonacci():
    temp = [1,1]
    while True:
        temp.append(sum(temp))
        yield temp.pop(0)  #yield是實現生成器的關鍵字

2)通過生成器管道模組化處理資料

def fibonacci():
    temp = [1,1]
    while True:
        temp.append(sum(temp))
        yield temp.pop(0) 

def dataflow():
    for x in fibonacci():
        yield x ** 2
if __name__ == '__main__':
    for x in dataflow():
        print(x)
        if x > 10000:
            break