专栏首页恩蓝脚本Python类中的装饰器在当前类中的声明与调用详解

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

执行结果如图

一个更完整的示例

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

以上这篇Python类中的装饰器在当前类中的声明与调用详解就是小编分享给大家的全部内容了,希望能给大家一个参考。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 在laravel框架中使用model层的方法

    创建model:php artisan make:model privilegeModel(名字随便写,可以不加Model)

    砸漏
  • Android利用AudioRecord类实现音频录制程序

    AudioRecord类相对于MediaRecorder来说,更加接近底层,为我们封装的方法也更少。然而实现一个AudioRecord的音频录制程序也很简单。本...

    砸漏
  • PHP生成指定范围内的N个不重复的随机数

    思路:将生成的随机数存入数组,再在数组中去除重复的值,即可生成一定数量的不重复随机数。

    砸漏
  • Python全栈开发之---装饰器

    你觉得碍眼,python的开发者也觉得碍眼,所以就为我们提供了一句语法糖来解决这个问题!

    py3study
  • 矢量Chart图表嵌入HTML5网络拓扑图的应用

    使用 HT for Web (以下简称 HT)开发HTML5网络拓扑图的开发者有 Chart 需求的项目的时候,感觉很痛苦,HT 集成的 Chart 组件中,并...

    HT for Web
  • 矢量Chart图表嵌入HTML5网络拓扑图的应用

    HT_hightopo
  • 移动开发之浅析cocos2d-x的中文支持问题

      题记:这阵子一直在学习cocos2d-x,其跨平台的特性确实让人舒爽,引擎的框架概念也很成熟,虽然相应的第三方工具略显单薄,但也无愧是一件移动开发的利器啊,...

    用户2615200
  • eclipse 中maven test运行后console中出现中文乱码问题解决方法

    在pom.xml中添加如下配置,保存后重新运行maven test就不会出现中文乱码了:

    克虏伯
  • HDFS详解

    【一】HDFS简介 HDFS的基本概念1.1、数据块(block) HDFS(Hadoop Distributed File System)默认的最基本的存储...

    猿人谷
  • 携程事件启发:监督和审计也是关键

      5月28日下午2点左右,针对携程网站无法打开的事件在朋友圈被刷屏。刚刚开始是各种调侃,其中要求对运维人员好一点的呼声最高、传播最广,然后是携程老板悬赏100...

    安恒信息

扫码关注云+社区

领取腾讯云代金券