1. 程式人生 > >Python基礎05 - 裝飾器

Python基礎05 - 裝飾器

重復 += home 函數式編程 問題 user col rap 重用

@@@文章內容參照老男孩教育  Alex金角大王,武Sir銀角大王@@@

函數即對象

  在python中,函數和我們之前的[1,2,3],‘abc‘,8 等一樣都是對象,而且函數是最高級的對象(對象是類的實例化,可以調用相應的方法,函數是包含變量對象的對象。)

高階函數

嵌套函數及閉包

1 def func():
2     x = 1
3     def foo():
4         print(x)
5     return foo
6 
7 a = func()
8 a()

閉包(closure)是函數式編程的重要的語法結構

  定義:如果在一個內部函數裏,對在外部作用域(但不是在全局作用域)的變量進行引用,那麽內部函數就被認為是閉包

如上實例,foo就是內部函數,foo裏引用了外部作用域的變量x(x在外部作用域func裏面,不是在全局作用域),則這個內部函數foo就是一個閉包。

代碼的開放封閉原則

  軟件開發中的一個原則“開放-封閉”原則,簡單來說,它規定已經實現的功能代碼不允許被修改,但可以被擴展,即:

  • 封閉:已實現的功能代碼塊不應該被修改
  • 開放:對現有功能的擴展開發

裝飾器概念

裝飾器本質上是一個函數,該函數用來處理其他函數,它可以讓其他函數在不需要修改代碼的前提下增加額外的功能,裝飾器的返回值也是一個函數對象。它經常用於有切面需求的場景,比如:插入日誌、性能測試、事務處理、緩存、權限校驗等應用場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。

 1 import time
 2 # 業務生產中大量調用的函數
 3 def foo():
 4     print(hello foo)
 5 foo()
 6 # 現在有一個新的需求,希望可以記錄下函數的執行時間,於是在代碼中添加日誌代碼
 7 def foo():
 8     start_time = time.time()
 9     time.sleep(2)
10     print(hello foo)
11     stop_time =time.time()
12     print(spend %s %(stop_time - start_time))
13 14 foo() 15 16 # bar()、bar2()也有類似的需求,怎麽做?再在bar函數裏調用時間函數? 17 # 這樣就造成大量雷同的代碼,為了減少重復寫代碼,我們可以這樣做,重新定義一個函數:專門設定時間 18 def show_time(func): 19 start_time = time.time() 20 func() 21 stop_time = time.time() 22 print(spend %s %(stop_time - start_time)) 23 24 def foo(): 25 time.sleep(2) 26 print(hello foo) 27 28 show_time(foo)

邏輯上不難理解,而且運行正常。但是這樣改變了調用方式,容易被人投訴,因為我們每次都要將一個函數作為參數傳遞給show_time函數。而且這種方式已經破壞了原有的代碼邏輯結構,之前執行業務邏輯進,執行foo(),但是現在不得不改成show_time(foo)。那麽有沒有更好的方式呢?當然有,答案就 是裝飾器。

簡單裝飾器

 1 import time
 2 def show_time(func):
 3     def wrapper():
 4         start_time = time.time()
 5         func()
 6         stop_time = time.time()
 7         print(spend %s %(stop_time - start_time))
 8 
 9     return wrapper
10 
11 # @符號是裝飾器的語法堂,在定義函數的時候使用,避免再一次賦值操作
12 @show_time  # foo = show_time(foo)
13 def foo():
14     time.sleep(2)
15     print(hello foo)
16 
17 foo()

裝飾器在python使用如此方便都要歸因於python函數能像普通的對象一樣能作為參數傳遞給其他函數,可以被賦值給其他變量,可以作為返回值,可以被定義在另外一個函數內。

帶參數的被裝飾函數

 1 import time
 2 def show_time(func):
 3     def wrapper(a,b):
 4         start_time = time.time()
 5         ret = func(a,b)
 6         stop_time = time.time()
 7         print(spend %s %(stop_time - start_time))
 8         return ret
 9 
10     return wrapper
11 
12 @show_time
13 def add1(a,b):
14     time.sleep(1)
15     print(a + b)
16 
17 add1(4,5)
18 
19 # 註意函數的返回值時
20 @show_time
21 def add2(a,b):
22     time.sleep(1)
23     return a + b
24 
25 print(add2(4,5))

不定長參數

 1 import time
 2 def show_time(func):
 3     def wrapper(*args,**kwargs):
 4         start_time = time.time()
 5         ret = func(*args,**kwargs)
 6         stop_time = time.time()
 7         print(spend %s %(stop_time - start_time))
 8         return ret
 9 
10     return wrapper
11 
12 @show_time
13 def add(*args,**kwargs):
14     time.sleep(1)
15     sum = 0
16     for i in args:
17         sum += i
18     print(sum)
19 
20 add(4,5,6,7,8,9)

帶參數的裝飾器

裝飾器還有更大的靈活性,例如帶參數的裝飾器;在上面的裝飾器調用中,比如@show_time,該裝飾器唯一的參數就是執行業務的函數。裝飾器的語法允許我們在調用時,提供其它參數,比如@auth(a)。這樣就為裝飾器的編寫和使用提供了更大的靈活性。

 1 user,passwd = abc,abc123 # 存用戶信息
 2 user_status = False # 用戶登錄了就把這個改成True
 3 
 4 def auth(auth_ytpe=0): # 把要執行的模塊從這裏傳進來
 5     def wrapper(func):
 6         def inner(*args,**kwargs): #
 7             if auth_ytpe == 1:
 8                 global user_status
 9                 if user_status == False:
10                     username = input(Username:).strip()
11                     password = input(Password:).strip()
12                     if username == user and password == passwd: # 用戶認證
13                         print(welcome login ......)
14                         user_status = True  # 登入成功改成True
15                     else:
16                         print(wrong username or password!)
17                 if user_status == True:
18                     return func(*args,**kwargs) # 驗證通過,調用相應功能函數
19             elif auth_ytpe == 2:
20                 pass
21         return inner
22     return wrapper
23 
24 @auth(0)
25 def index():
26     print(welcome to index)
27 
28 @auth(1)
29 def home(a):
30     print(welcome to home %s%(a))
31     return home
32 
33 @auth(2)
34 def bbs():
35     print(welcome to bbs)
36 
37 index()
38 print(home(33))
39 bbs()

多層裝飾器

 1 def w1(fnuc):
 2     def wrapper():
 3         return <b> + fnuc() + </b>
 4 
 5     return wrapper
 6 
 7 def w2(fnuc):
 8     def wrapper():
 9         return <i> + fnuc() + </i>
10 
11     return wrapper
12 
13 @w1
14 @w2
15 def hello():
16     return hello
17 
18 print(hello())

Python基礎05 - 裝飾器