1. 程式人生 > 實用技巧 >python-迭代器協議和for迴圈工作機制

python-迭代器協議和for迴圈工作機制

一、遞迴與迭代

二、什麼是迭代器協議

1、迭代器協議是指:物件必須提供一個next方法,執行該方法要麼返回迭代中的下一項,要麼就引起一個stopiteration異常,已終止迭代(只能往後走不能往前退)

2、可迭代物件:實現了迭代器協議的物件(如何實現:物件內部定義一個__iter__()方法)

3、協議是一種約定,可迭代物件實現了迭代器協議,python的內部工具(如for迴圈,sum,min,max函式等)使用迭代器協議訪問物件。

三、python中強大的for迴圈機制

for迴圈的本質:迴圈所有物件,全部是使用迭代器協議

解釋:

有時會想,for迴圈的本質就是遵循迭代器協議訪問物件,那麼for迴圈的物件肯定都是迭代器了啊,沒錯,那既然這樣,for迴圈可以遍歷(字串,,列表,字典,集合,檔案物件),那這些型別的資料肯定都是可迭代物件啊?但是,為什麼定義一個列表l=[1,2,3,4]沒有next()方法。

(字串,列表,元組,字典,集合,檔案物件)這些都不是可迭代物件,只不過在for迴圈中,呼叫了他們內部的__iter__方法,把他們變成了可迭代物件

然後for迴圈呼叫可迭代物件的__next__方法去取值,而且for迴圈會捕捉stoplteration異常,已終止迭代

l=[1,2,3,4,5]
#下標訪問方式
print(l[0])
print(l[7])  #超出訪問會報IndexError: list index out of range

#遵循迭代器協議的方式
diedai=l.__iter__()
print(diedai.__next__())
print(diedai.__next__
()) print(diedai.__next__()) print(diedai.__next__()) print(diedai.__next__()) print(diedai.__next__()) #超出邊界會報StopIteration #for迴圈訪問方式: #for迴圈本質就是遵循迭代器協議的訪問方式,先呼叫diedai.__iter__()方法,或者直接diedai=iter(l),然後依次執行diedai.next(),直到for迴圈捕捉到StopIteration終止迴圈 #for迴圈所有物件的本質都是一樣的道理 for i in l: #diedai=l.__iter__()
print(l[i]) #i=diedai.next() #使用while模擬for迴圈做的事情 diedai_l=l.__iter__() while True: try: print(diedai_l.__next__()) except StopIteration: print("迭代完畢,終止迴圈") break

四、生成器初探

什麼是生成器?

可以理解為一種資料型別,這種資料型別自動實現了迭代器協議(其他的資料型別需要呼叫自己內建的__iter__方法),所以生成器就是可迭代物件

生成器分類及在python中的表現形式:(python有兩種不同的方法提供生成器)

1、生成器函式:常規函式定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在沒個結果中間,掛起函式的狀態,以便下次用它離開的地方繼續執行

2、生成器表示式:類似於列表推導,但是,生成器返回按需產生結果的一個物件,而不是一次構建一個結果列表

為何使用生成器以及生產器的優點:

python使用生成器對延遲操作提供了支援,所謂延遲操作,是指在需要的時候才產生結果,而不是立即產生結果,這也是生產器的重要好處

import time
# def producer():
#     ret=[]
#     for i in range(100):
#         time.sleep(0.1)
#         ret.append('包子%s' %i)
#     return ret
#
# def consumer(res):
#     for index,baozi in enumerate(res):
#         time.sleep(0.1)
#         print('第%s個人,吃了%s' %(index,baozi))
#
# res=producer()
# consumer(res)




#yield 3相當於return 控制的是函式的返回值
#x=yield的另外一個特性,接受send傳過來的值,賦值給x
# def test():
#     print('開始啦')
#     firt=yield #return 1   first=None
#     print('第一次',firt)
#     yield 2
#     print('第二次')
#
# t=test()
# res=t.__next__() #next(t)
# print(res)
# # t.__next__()
# # res=t.send(None)
# res=t.send('函式停留在first那個位置,我就是給first賦值的')
# print(res)





# def producer():
#     ret=[]
#     for i in range(100):
#         time.sleep(0.1)
#         ret.append('包子%s' %i)
#     return ret

def consumer(name):
    print('我是[%s],我準備開始吃包子了' %name)
    while True:
        baozi=yield
        time.sleep(1)
        print('%s 很開心的把【%s】吃掉了' %(name,baozi))

def producer():
    c1=consumer('wupeiqi')
    c2=consumer('yuanhao_SB')
    c1.__next__()
    c2.__next__()
    for i in range(10):
        time.sleep(1)
        c1.send('包子 %s' %i)
        c2.send('包子 %s' %i)
producer()

生產器小結

1、生成器是可迭代物件

2、實現了延遲計算、省記憶體

3、生成器本質和其他的資料型別一樣,都是實現了迭代器協議,只不過生成器附加了一個延遲計算省記憶體的好處,其餘的可迭代物件可沒有這點好處

五、生成器表示式和列表解析

#1、三元表示式
name="alex"
name="yangyl"
res="1" if name=="yangyl" else "2"
print(res)

egg_list=["雞蛋%s" %i for i in range(10) ]    #列表解析
print(egg_list)

#使用生產器獲取
egg_two=("雞蛋%s" %i for i in range(10))      #生產器表示式
print(egg_two)
print(egg_two.__next__())
print(next(egg_two))            #next()本質就是呼叫__next__

總結:

1、把列表解析中的[]換成() 得到的就是生成器表示式

2、列表解析與生成器表示式都是一種便利的程式設計方式,只不過生成器表示式更節省記憶體

3、python不但使用迭代器協議,讓for迴圈變得更加通用。大部分內建函式,也是使用迭代器協議訪問物件的。列如:sum函式是python的內建函式,該函式使用迭代器協議訪問物件,而生成器實現了迭代器協議,所以我們可以直接這樣計算一系列值的和:

s1=sum(x ** 2 for x in range(4))
print(s1)

而不用多此一舉先構造一個列表

s2=sum([x ** 2 for x in range(4)])
print(s2)