Python类中的装饰器在当前类中的声明与调用详解
(编辑:jimmy 日期: 2024/11/18 浏览:3 次 )
我的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, *args, **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, value, 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, value, new_attrs) return new_attrs def __new__(cls, name, bases, attrs): new_attrs = cls.options(bases, attrs) return super(Meta, cls).__new__(cls, name, bases, 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", line 37, 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 37, in inner return func(*args, **kwargs) 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", line 37, in inner EXCEPTION captured at function seven return func(*args, **kwargs) File "E:/pydev/异常处理装饰器.py", line 115, in one w = 1 / 0 ZeroDivisionError: integer division or modulo by zero Traceback (most recent call last): File "E:/pydev/异常处理装饰器.py", line 37, in inner return func(*args, **kwargs) 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 37, in inner return func(*args, **kwargs) 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 37, in inner return func(*args, **kwargs) 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 37, in inner return func(*args, **kwargs) File "E:/pydev/异常处理装饰器.py", line 37, in inner return func(*args, **kwargs) 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 37, in inner return func(*args, **kwargs) 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/", line 212, in <module> m.five() File "E:/pydev/", line 104, in five w = 1 / 0 ZeroDivisionError: integer division or modulo by zero 进程已结束,退出代码 1
以上这篇Python类中的装饰器在当前类中的声明与调用详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
下一篇:如何提高python 中for循环的效率