1. 程式人生 > >迭代器與裝飾器

迭代器與裝飾器

一. iter&yield迭代器

    1.1 iter

複製程式碼
 1 names = iter(['zeng', 'chun', 'yun'])
 2 print(names)
 3 print(names.__next__())
 4 print(names.__next__())
 5 print(names.__next__())
 6 print(names.__next__())
 7 
 8 
 9 #第一個輸出列印迭代器物件
10 #第二三四次next方法每次都去獲取迭代物件的值,每次往下取一個值,直到取完
11 #第五次輸出    print(names.__next__())
12 #StopIteration,因為迭代器裡面的物件已經取完了,所以出現這個異常
複製程式碼

   1.2 yield

複製程式碼
 1 def fei(num):
 2     x, y, z = 0, 0, 1
 3     while x < num:
 4         if x == 0:  # 當第一次迴圈時,返回數列0
 5             yield y  # 生成器返回一個迭代器物件
 6         else:  # 否則返回z
 7             yield z
 8             y, z = z, y + z
9 x += 1 10 11 print(fei(9).__next__()) # 只從迭代器裡面取一次值 12 # fei(9).__next__() 13 for i in fei(9): # 通過遍歷獲取迭代器內容 14 print(i)
複製程式碼

 

   1.3  生成器的非同步應用

複製程式碼
import time


def consumer(name):  # 定義消費者函式
    print("%s 準備吃包子啦!" % name)  # 列印消費者名字
    while True:
       baozi 
= yield # 當代碼執行到這時,返回一個迭代器物件,當物件第二次呼叫時,會接收值並賦值給baozi print("包子[%s]來了,被[%s]吃了!" % (baozi, name)) # 列印當前使用的迭代器物件,及接收的值 def producer(*name): # 定義一個生產者函式,並使用動態引數 if len(name) == 3: # 當傳入三個引數時,呼叫三次消費者函式方法 c = consumer(name[0]) c2 = consumer(name[1]) c3 = consumer(name[2]) c.__next__() # 通過函式內建方法獲取迭代器內部元素 c2.__next__() c3.__next__() print("老子開始準備做包子啦!") for i in range(1, 11): time.sleep(1) print("做了%s個包子!" % len(name)) c.send(i) # 通過迭代器方法的send屬性給生成器傳送值 c2.send(i) c3.send(i) producer("alex", 'zengchunyun', 'peiqi') # 建立一個生產者物件,並傳入三個消費者名字
複製程式碼

 

  二. 裝飾器

    2.1 首先了解一下函式

複製程式碼
def func():
    print('hello')

print(func)  # 第一次輸出的是一個函式物件
print(type(func))  #第二次輸出的是一個型別為函式
func()  # 第三次才是執行這個函式
print(func())  #第四次則是執行函式後,並列印函式的返回結果,函式沒有指定返回內容,所以使用預設返回值,None
print(type(func()))  # 第五次也是先執行函式,然後列印返回值物件的型別,所以可以看出,返回值物件型別是NoneType

# 第一次
#<function func at 0x100756f28>
# 第二次
#<class 'function'>
# 第三次
#hello
# 第四次
#hello
#None
# 第五次
#hello
#<class 'NoneType'>
複製程式碼

    通過上述總結一句話,函式不加括號是沒有執行的,這時的這個函式名只是一個函式物件,而加括號後,函式將會執行函式體程式碼,

  最終這個函式則是函式執行完後的返回的物件

    2.2 裝飾器

    有了上面這個基礎後,我們再來看看裝飾器

複製程式碼
 1 def wrapper(func):
 2     print(func)  # 列印引數
 3     return func  # 返回引數
 4 
 5 
 6 @wrapper  # 使用裝飾器
 7 def index():
 8     print('welcome')
 9 
10 
11 index()
12 
13 # 執行順序是,直譯器從上往下讀取程式碼
14 # 遇到函式時,只加載定義的函式物件,並不執行函式體程式碼
15 # 然後遇到裝飾器@wrapper時
16 # 直譯器會跳到這個wrapper函式
17 # 然後執行這個wrapper函式內部程式碼
18 # 我們通過觀察得知,這個wrapper函式一定會傳入一個引數,因為測試發現,不傳入一個引數,程式執行會丟擲需要一個引數的異常錯誤.
19 # 通過分析這個引數,發現這個引數列印結果是一個函式物件
20 # 然後wrapper函式體程式碼執行完畢後,繼續往下執行,遇到函式index,
21 # 也是隻是載入這個函式物件,並不執行內部函式體程式碼
22 # 當遇到程式碼index()時,結合到我們之前積累的函式基礎知識,
23 # 這個寫法實際是開始執行一個函式,所以直譯器會跳到指定的index函式物件
24 # 然後開始執行這個函式體程式碼塊,
25 # 整個執行過程就結束了
26 
27 
28 # 以下為程式碼執行結果
29 # <function index at 0x1010ed0d0>
30 # welcome
複製程式碼

    可能你對上面這個過程還是不夠了解裝飾器,那麼我們再通過下面這個例子說明裝飾器的工作原理

複製程式碼
 1 def wrapper(func):
 2     print(func)  # 列印引數
 3     # return func  # 返回引數 ,現在註釋掉這個返回值
 4 
 5 
 6 @wrapper  # 使用裝飾器
 7 def index():
 8     print('welcome')
 9 
10 print(type(index))  # 加上一句輸出型別程式碼的語句
11 index()
12 
13 #我們在上面這個例子中,把函式wrapper中的返回值給註釋掉了,程式碼執行結果
14 
15 # function index at 0x1011ed0d0>
16 # <class 'NoneType'>
17 # Traceback (most recent call last):
18 #   File "/Users/zengchunyun/PycharmProjects/s12/day4/noteext.py", line 68, in <module>
19 #     index()
20 # TypeError: 'NoneType' object is not callable
21 
22 # 首先我們來分析一下這個結果
23 # 你會不會很驚訝?我們只是針對第一個例子的程式碼僅僅只是註釋掉一個返回值而已,程式碼就不能工作了
24 
25 # 首先直譯器還是從上往下讀取程式碼
26 # 遇到函式時,只加載定義的函式物件,並不執行函式體程式碼
27 # 然後遇到裝飾器@wrapper時
28 # 直譯器會跳到這個wrapper函式
29 # 然後執行這個wrapper函式內部程式碼
30 # 通過分析這個引數,發現這個引數列印結果是一個函式物件
31 # 然後wrapper函式體程式碼執行完畢後,繼續往下執行,遇到函式index,
32 # 也是隻是載入這個函式物件,並不執行內部函式體程式碼
33 # 關鍵點來了
34 # 程式碼執行到列印物件型別的語句時,結果卻是一個NoneType的型別,根據我們之前對函式的基本介紹,這裡的型別應該是一個函式型別才對啊
35 # 怎麼回事呢?我們明明定義了index函式,怎麼列印的型別卻是NoneType型別?
36 # 我們之前也看到只有函式沒有返回值時,函式預設會返回一個None物件,故而這個物件的型別也就是NoneType型別了,
37 # 我們僅僅只是加了一個裝飾器程式碼@wrapper,其他都沒有變,為什麼會出現這個情況呢
38 # 我們上一個例子已經說明,這個裝飾器會攜帶一個引數,這個引數為一個函式物件,
39 # 實際上,這個時候這個裝飾器會對引用裝飾器的函式,也就是我們這裡的index函式進行重構,
40 # 所以如果我們如果不返回一個函式物件時,那麼這個時候的index實質是一個普通的物件,不是函式型別了
41 # 它已經被賦予None這個值了,而None不是一個函式物件,所以就沒有呼叫方法,就不能以括號方式執行
42 # 這時直譯器讀到index()這句程式碼,大家依據之前的理念,都能看出這個是去執行index這個函式內部程式碼塊的語句
43 # 但是這個時候,直譯器卻在這個時候丟擲異常了
44 # 返回型別錯誤,TypeError: 'NoneType' object is not callable
45 # 這個錯誤說我們的index執行後,是不能被呼叫的,只有物件型別為函式才有內建呼叫方法
46 # 因為這個index已經被重構,返回值已經變成了None,也就是說,index 物件目前僅僅是一個普通識別符號,不是函式
複製程式碼

    2.3 裝飾器的高階應用

      我們通過上面的事咧可知,只要程式碼執行到裝飾器識別符號,都會去執行裝飾器函式體,但是這個不是我們想要的,我們希望的是

    只有我們呼叫引用裝飾器的函式時,才去執行這個裝飾器函式體,那怎麼辦呢?我們知道,只有型別是函式物件時,程式碼是不會被執行,

    只是載入到記憶體而已,那好吧,我們直接返回一個函式物件不就好了?

      我們來看看程式碼實現

複製程式碼
 1 def wrapper(func):
 2     def inner():
 3         print(func)  # 輸出是一個函式物件
 4         func()  # 這裡實際是執行我們這個例子中原先定義的index函式物件的函式體
 5     return inner
 6     # print(func)  # 列印引數
 7     # return func  # 返回引數 ,現在註釋掉這個返回值
 8 
 9 
10 @wrapper  # 使用裝飾器
11 def index():
12     print('welcome')
13 
14 print(type(index))  # 加上一句輸出型別程式碼的語句
15 index()
16 
17 # 首先看看這種情況
18 # 程式碼執行到index()時,啥也沒有,我們明明列印了一句welcome,為什麼沒有輸出資訊,這也就再次證明了,
19 # 實質是這個index已經等價於裝飾器的inner這個函數了,因為裝飾器返回的是inner這個函式物件
20 # 我們既想用裝飾器,又想執行呼叫裝飾器的內部程式碼時怎麼辦呢?
21 # 我們已經知道,裝飾器會攜帶一個引數,這個引數是引用裝飾器物件的一個函式物件
22 # 既然是一個函式物件,那我們是不是可以直接執行這個函式物件呢?
23 # 答案是肯定的,所以我們直接在這個裝飾器裡執行這個函式物件,等於就是執行我們原先那個函式的函式體
24 # 這個例子就滿足了我們的需求,當我們不呼叫index函式時,得到的僅僅是一個函式物件,並不會執行函式體程式碼
複製程式碼

 

  有引數的函式,或裝飾器實現傳參例項

 1 # 無參裝飾器,有參函式
 2 def wrapper(func):
 3     def inner(name):  # 這個引數最終會傳給這個函式體內部需要呼叫引數的物件
 4         func(name)  # 這個引數個數是由原來的函式,也就是我們這裡的index函式決定引數個數的
 5     return inner
 6 
 7 
 8 @wrapper  # 使用裝飾器
 9 def index(name):  # 傳入一個引數
10     print('welcome %s' % name)
11 
12 index('zengchunyun')
無參裝飾器,有參函式
 1 # 無參裝飾器,多參函式
 2 def wrapper(func):
 3     def inner(*args):  # 使用動態引數
 4         func(*args)
 5     return inner
 6 
 7 
 8 @wrapper  # 使用裝飾器
 9 def index(*args):  # 傳入一個引數
10     print('welcome %s' % ' '.join(args))
11 
12 index('zengchunyun', 'goodbye')
無參裝飾器,多參函式
 1 # 無參裝飾器,多參函式2
 2 def wrapper(func):
 3     def inner(*args, **kwargs):  # 使用動態引數
 4         func(*args, **kwargs)
 5     return inner
 6 
 7 
 8 @wrapper  # 使用裝飾器
 9 def index(*args, **kwargs):  # 傳入一個引數
10     print('welcome %s' % ' '.join(args))
11 
12 index('zengchunyun', 'goodbye')
無參裝飾器,多參函式2
 1 # 有參裝飾器,多參函式
 2 def one():
 3     print('one')
 4 
 5 
 6 def two():
 7     print('two')
 8 
 9 
10 def func(arg1, arg2):
11     def wrapper(oldfunc):
12         def inner(*args, **kwargs):  # 使用動態引數
13             arg1()
14             arg2()
15             oldfunc(*args, **kwargs)
16         return inner
17     return wrapper
18 
19 
20 @func(one, two)  # 使用裝飾器
21 def index(*args, **kwargs):  # 傳入一個引數
22     print('welcome %s' % ' '.join(args))
23 
24 index('zengchunyun', 'goodbye')
25 
26 # 直譯器遇到裝飾器,由於這個裝飾器是一個可執行函式
27 # 故而先執行函式,再次就成了我們所認知的普通裝飾器了
# 有參裝飾器,多參函式