裝飾者模式是常用的軟體設計模式之一,通過此設計模式,我們能夠在不修改任何底層代碼情況下,給已有物件賦予新的職責,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實戰教程免非下,,一起相互監督共同進步!
本文的文字及圖片來源于網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,著作權歸原作者所有,如有問題請及時聯系我們以作處理,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/167817.html
標籤:Python
下一篇:python序列(六)串列排序
