Python裝飾器的使用【面試必學】


裝飾者模式是常用的軟件設計模式之一。通過此設計模式,我們能夠在不修改任何底層代碼情況下,給已有對象賦予新的職責。python中可以用裝飾器簡單地實現裝飾者模式。

PS注意:很多人學Python過程中會遇到各種煩惱問題,沒有人解答容易放棄。為此小編建了個Python全棧免費答疑.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,不懂的問題有老司機解決里面還有最新Python實戰教程免非下,,一起相互監督共同進步!

1.1 將函數作為參數傳遞

C/C++中,函數指針可以將函數作為參數傳遞給另一函數。而在python中,函數也是對象的一種,函數可以被引用,也可直接作為參數傳入函數,以及作為容器對象的元素。python中可以采用如下方法實現裝飾者模式:

#!/usr/bin/env python3.6 # -*- coding: utf-8 -*- def add(x, y): result = x+y return result def log(func): def wrapper(*args, **kwargs): result = func(*args) print(func.__name__,'has been called\n') return result return wrapper if __name__ == '__main__': print(log(add)(1,2)) 

上述代碼中,log函數以需要被裝飾的函數作為參數,並返回函數對象。被返回的函數的參數為可變參數*args**kwargs*args參數會被封裝成tuple**kwargs參數則會被封裝成字典對象),以適應不同函數的不同參數,保證通用性。

1.2 裝飾器

上面的實現方法有些繁雜,所有調用被裝飾的函數之處的代碼,都要進行相應修改,自然不符合python簡潔易讀的特性。因此python中給出相應語法糖來增加可讀性和易用性,那便是“裝飾器”。

#!/usr/bin/env python3.6 # -*- coding: utf-8 -*- from functools import wraps def log(func): #@wraps(func) def wrapper(*args, **kwargs): result = func(*args) print(func.__name__,'has been called') return result return wrapper #等價於add = log(add) @log def add(x, y): result = x+y return result if __name__ == '__main__': print(add(1,2)) print(add.__name__) 

運行情況如下

>>print(add(1,2)) add has been called 3 >>print(add.__name__) wrapper 

但上述方法亦有缺陷,原函數add的元數據(比如名字、文檔字符串、注解和參數簽名)會丟失。為避免缺陷,任何時候你定義裝飾器的時候,都應該使用functools庫中的@wraps裝飾器來注解底層包裝函數(代碼中注釋部分)。@wraps有一個重要特征是它能讓你通過屬性 __wrapped__ 直接訪問被包裝函數。
改進后運行情況

>>print(add(1,2)) add has been called 3 >>print(add.__name__) add 

1.3 解除裝飾器

當裝飾器已經作用於某函數,而你想撤銷它,那么可以訪問 __wrapped__屬性來訪問原始函數

orig_add = add.__wrapped__ orig_add(1,2) 

但若使用了多個裝飾器, __wrapped__屬性會變得不可控,應盡量避免使用。
若有如下代碼:

#!/usr/bin/env python3.6 # -*- coding: utf-8 -*- import functools import time def metric(func): @functools.wraps(func) def wrapper(*args,**kv): print('Decorator1') f = func(*args,**kv) return f return wrapper def logging(func): @functools.wraps(func) def wrapper(*args,**kv): print('Decorator2') f = func(*args,**kv) return f return wrapper @metric @logging def normalize(name): sName = name[0:1].upper() + name[1:].lower() print(sName) if __name__ == '__main__': normalize('heLlO') normalize.__wrapper__('') 

運行情況如下

>>normalize('helLo') Decorator1 Decorator2 Hello >>normalize.__wrapped__('world') Decorator2 World 

1.4 定義帶參數的裝飾器

from functools import wraps def log(text): def decorator(func): @wraps(func) def wrappering(*args,**kv): print('%s %s():'%(text,func.__name__)) return func(*args,**kv) return wrappering return decorator @log('run') def normalize(name): sName = name[0:1].upper() + name[1:].lower() print(sName) 

裝飾器函數可以帶參數,最外層的函數會將參數傳給內層的裝飾器函數,即wrappering函數是可以使用log的傳入參數的。
裝飾器處理過程與下面是等價的:

normalize = log('run')(normalize)

總結注意:很多人學Python過程中會遇到各種煩惱問題,沒有人解答容易放棄。為此小編建了個Python全棧免費答疑.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,不懂的問題有老司機解決里面還有最新Python實戰教程免非下,,一起相互監督共同進步!

本文的文字及圖片來源於網絡加上自己的想法,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯系我們以作處理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM