Python類中的裝飾器在當前類中的宣告與呼叫詳解
阿新 • • 發佈:2020-04-16
我的Python環境:3.7
在Python類裡宣告一個裝飾器,並在這個類裡呼叫這個裝飾器。
程式碼如下:
class Test(): xx = False def __init__(self): pass def test(func): def wrapper(self,*args,**kwargs): print(self.xx) return func(self,**kwargs) return wrapper @test def test_a(self,a,b): print(f'ok,{a} {b}')
注意:
1. 其中裝飾器test是在類Test中宣告並在其方法test_a中呼叫
2. 裝飾器test內層wrapper函式的首引數是self
補充知識:python-類內函式的全域性裝飾器
有時,比如寫RF的測試庫的時候,很多方法都寫在一個類裡。我們又可能需要一個通用的裝飾器,比如,要給某個底層類的方法打樁,檢視入參和出參,用以理解業務;或者要hold住所有的執行錯誤,列印堆疊又不想程式退出或用例直接失敗
比如捕捉錯誤的裝飾器
import traceback from functools import wraps def trier(soft=False): ''' :param bool soft: 為True時,列印報錯堆疊並忽略異常。預設False,列印報錯堆疊並丟擲異常 :return: 如果要給類方法、靜態方法裝飾,則該裝飾器必須處於比@staticmethod裝飾器更內一層才行 ''' def realTrier(func): ''' :param function func: :return: ''' @wraps(func) # 保留__name__ __doc__ __module__ def innerfunc(*args,**kwargs): try: return func(*args,**kwargs) except Exception,e: try: print(traceback.format_exc()) except: print e if not soft: raise return innerfunc return realTrier
或者引數跟蹤的裝飾器
def tracer(func): def infunc(*args,**kwargs): print func.__name__,args,kwargs res=infunc(*args,**kwargs) print func.__name__,res return res
這類裝飾器經常會給類裡的每個函式都使用
每次都裝飾的話,也挺麻煩
python裡可以給類寫個裝飾器,所以可以輸入一個類,返回一個新類,這個新類擁有原來類裡的所有方法,但所有方法都被裝飾
使用元類,可以做到這一點。
目前可以批量裝飾普通方法、靜態方法、類方法、屬性,暫不支援__init__和__del__之類的特殊方法,以免出現意外的問題。
目前類B使用了全域性裝飾器,假如類B繼承自類A,類C繼承自類B
則類B、類C內的所有方法都被全域性裝飾(全域性裝飾可以被繼承)
且類B繼承自類A的所有方法也會被全域性裝飾
但這種裝飾不會影響到類A,呼叫類A下的方法時,所有方法都不被裝飾
經過多次嘗試,最後的實現程式碼如下
# clswrapper.py def skipper(func): ''' :param function func: :return: ''' func.__funskip__=True return func def classWrapper(commonDecoratorFunc): def innerMata(inClass): def collect_attrib(key,value,new_attrs): if hasattr(value,'__funskip__'): new_attrs[key] = value return if hasattr(value,'__func__') or isinstance(value,types.FunctionType): if isinstance(value,staticmethod): new_attrs[key] = staticmethod(commonDecoratorFunc(value.__func__)) return elif isinstance(value,classmethod): new_attrs[key] = classmethod(commonDecoratorFunc(value.__func__)) return elif not key.startswith('__'): new_attrs[key] = commonDecoratorFunc(value) return else: if isinstance(value,property): # 當對property類進行重組的時候,我們強制裝飾了property類的fget fset和fdel方法。但是,不是每個propery都有這三個方法,有些是None,強制裝飾會報錯,所以我們這裡要考慮提前返回None propertyWrapper = property(fget=commonDecoratorFunc(value.fget) if value.fget else None,fset=commonDecoratorFunc(value.fset) if value.fset else None,fdel=commonDecoratorFunc(value.fdel) if value.fdel else None,doc=value.__doc__) new_attrs[key] = propertyWrapper return new_attrs[key] = value class Meta(type): @classmethod def options(cls,bases,attrs): new_attrs = {} for key,value in attrs.items(): collect_attrib(key,new_attrs) for base in bases: for mbase in base.mro(): for key,value in mbase.__dict__.items(): if key not in new_attrs: collect_attrib(key,new_attrs) return new_attrs def __new__(cls,name,attrs): new_attrs = cls.options(bases,attrs) return super(Meta,cls).__new__(cls,new_attrs) return six.add_metaclass(Meta)(inClass) return innerMata
其中,skipper提供了一個後門,被skipper裝飾的函式會跳過全域性裝飾器
使用方法如下
@classWrapper(trier(soft=True)) class Tree(object): @skipper def div(self): return 1/0 def divsafe(self): return 1/0 t=Tree() print t.divsafe() print t.div()
執行結果如圖
一個更完整的示例
from clswrapper那個檔案 import skipper,classWrapper import traceback from functools import wraps '''為簡潔起見,這次我們用的是不帶引數的trier裝飾器''' def trier(func): @wraps(func) def inner(*args,**kwargs): try: return func(*args,**kwargs) except: print("EXCEPTION captured at function %s" % func.__name__,file=sys.stderr) print(traceback.format_exc().decode("gbk")) raise return inner if __name__=="__main__": import time class mobj(object): def five(self): w = 1 / 0 class obj(mobj): def __init__(self): # print "obj.__init__" return @classmethod def one(self): w = 1 / 0 print('obj.one') @classWrapper(trier) # 或者用@classWrapper(argTrier(True))替換,則可以不丟擲異常 class obj1(obj): aa = 1 def __init__(self): super(obj1,self).__init__() self.var = 1 @classmethod def three(cls): w = 1 / 0 print('obj1.three') @staticmethod def four(): w = 1 / 0 print('obj1.four') def two(self): w = 1 / 0 print(self.pro) print('obj1.two') @property def pro(self): return self.var @pro.setter def pro(self,value): self.var = value / 0 @skipper def eight(self): w=1/0 return w class outerobj(obj1): def seven(self): return 1/0 b = obj1() a = obj1 print(b.var) try: b.two() except: pass try: a.three() except: pass try: a.four() except: pass try: a.one() except: pass try: b.five() except: pass try: b.pro = 3 except: pass print(b.pro) print(a.aa) c=outerobj() try: c.five() except: pass try: c.seven() except: pass try: c.eight() except: print("c.eight被跳過,所以沒有被裡層捕獲,才會不打堆疊直接走到這裡") print("最後這個會真正觸發異常,因為mobj例項並沒有被裝飾過") m=mobj() time.sleep(1) m.five()
它展示了這個強大裝飾器能處理的各種情況,執行結果應該如下
1 EXCEPTION captured at function two EXCEPTION captured at function three Traceback (most recent call last): EXCEPTION captured at function four File "E:/pydev/異常處理裝飾器.py",line 37,in inner EXCEPTION captured at function one return func(*args,**kwargs) EXCEPTION captured at function five File "E:/pydev/異常處理裝飾器.py",line 138,in two w = 1 / 0 ZeroDivisionError: integer division or modulo by zero Traceback (most recent call last): File "E:/pydev/異常處理裝飾器.py",in inner return func(*args,**kwargs) File "E:/pydev/異常處理裝飾器.py",line 129,in three w = 1 / 0 ZeroDivisionError: integer division or modulo by zero Traceback (most recent call last): File "E:/pydev/異常處理裝飾器.py",line 134,in four w = 1 / 0 EXCEPTION captured at function pro ZeroDivisionError: integer division or modulo by zero EXCEPTION captured at function five Traceback (most recent call last): EXCEPTION captured at function five File "E:/pydev/異常處理裝飾器.py",in inner EXCEPTION captured at function seven return func(*args,line 115,in one w = 1 / 0 ZeroDivisionError: integer division or modulo by zero Traceback (most recent call last): File "E:/pydev/異常處理裝飾器.py",line 104,in five w = 1 / 0 ZeroDivisionError: integer division or modulo by zero Traceback (most recent call last): File "E:/pydev/異常處理裝飾器.py",line 148,in pro self.var = value / 0 ZeroDivisionError: integer division or modulo by zero 1 1 Traceback (most recent call last): File "E:/pydev/異常處理裝飾器.py",line 157,in seven return 1/0 ZeroDivisionError: integer division or modulo by zero c.eight被跳過,所以沒有被裡層捕獲,才會不打堆疊直接走到這裡 最後這個會真正觸發異常,因為mobj例項並沒有被裝飾過 Traceback (most recent call last): File "E:/pydev/�쳣����װ����.py",line 212,in <module> m.five() File "E:/pydev/�쳣����װ����.py",in five w = 1 / 0 ZeroDivisionError: integer division or modulo by zero 程序已結束,退出程式碼 1
以上這篇Python類中的裝飾器在當前類中的宣告與呼叫詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。