网络编程
位置:首页>> 网络编程>> Python编程>> Python类中的装饰器在当前类中的声明与调用详解

Python类中的装饰器在当前类中的声明与调用详解

作者:天外归云  发布时间:2021-03-07 10:29:35 

标签:Python,装饰器,声明,调用

我的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()


执行结果如图

Python类中的装饰器在当前类中的声明与调用详解

一个更完整的示例


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/�쳣����װ����.py", line 212, in <module>
 m.five()
File "E:/pydev/�쳣����װ����.py", line 104, in five
 w = 1 / 0
ZeroDivisionError: integer division or modulo by zero

进程已结束,退出代码 1

来源:https://www.cnblogs.com/LanTianYou/p/9957516.html

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com