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


Posted in Python onApril 15, 2020

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

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

Python 相关文章推荐
Python实现的石头剪子布代码分享
Aug 22 Python
详解在Python程序中使用Cookie的教程
Apr 30 Python
总结网络IO模型与select模型的Python实例讲解
Jun 27 Python
Python利用ElementTree模块处理XML的方法详解
Aug 31 Python
基于DataFrame改变列类型的方法
Jul 25 Python
TensorFlow Session使用的两种方法小结
Jul 30 Python
python画图系列之个性化显示x轴区段文字的实例
Dec 13 Python
python 获取微信好友列表的方法(微信web)
Feb 21 Python
Python实现微信消息防撤回功能的实例代码
Apr 29 Python
Python基础之列表常见操作经典实例详解
Feb 26 Python
Python基本数据类型之字符串str
Jul 21 Python
python playwrigh框架入门安装使用
Jul 23 Python
如何提高python 中for循环的效率
Apr 15 #Python
ipython jupyter notebook中显示图像和数学公式实例
Apr 15 #Python
解决 jupyter notebook 回车换两行问题
Apr 15 #Python
Python使用monkey.patch_all()解决协程阻塞问题
Apr 15 #Python
使用python处理题库表格并转化为word形式的实现
Apr 14 #Python
浅谈Python中re.match()和re.search()的使用及区别
Apr 14 #Python
Jupyter加载文件的实现方法
Apr 14 #Python
You might like
第八节--访问方式
2006/11/16 PHP
php数组合并与拆分实例分析
2015/06/12 PHP
php ajax数据传输和响应方法
2018/08/21 PHP
如何在centos8自定义目录安装php7.3
2019/11/28 PHP
javascript应用:Iframe自适应其加载的内容高度
2007/04/10 Javascript
JS上传图片前的限制包括(jpg jpg gif及大小高宽)等
2012/12/19 Javascript
Javascript保存网页为图片借助于html2canvas库实现
2014/09/05 Javascript
PhotoShop给图片自动添加边框及EXIF信息的JS脚本
2015/02/15 Javascript
用file标签实现多图文件上传预览
2017/02/14 Javascript
Javascript实现base64的加密解密方法示例
2017/06/27 Javascript
Vue多种方法实现表头和首列固定的示例代码
2018/02/02 Javascript
vue 使用Jade模板写html,stylus写css的方法
2018/02/23 Javascript
JavaScript实现选项卡效果的分析及步骤
2019/04/16 Javascript
详解vue2.0模拟后台json数据
2019/05/16 Javascript
vue递归获取父元素的元素实例
2020/08/07 Javascript
jQuery实现简单弹幕制作
2020/12/10 jQuery
Vue解决移动端弹窗滚动穿透问题
2020/12/15 Vue.js
举例详解Python中threading模块的几个常用方法
2015/06/18 Python
Python 实现淘宝秒杀的示例代码
2018/01/02 Python
使用 Python 在京东上抢口罩的思路详解
2020/02/27 Python
python实现二分类和多分类的ROC曲线教程
2020/06/15 Python
如何清空python的变量
2020/07/05 Python
Python QTimer实现多线程及QSS应用过程解析
2020/07/11 Python
css3实现椭圆轨迹旋转的示例代码
2018/10/29 HTML / CSS
全球最大的网上自行车商店:Chain Reaction Cycles
2016/12/02 全球购物
中国高端鲜花第一品牌:roseonly(一生只送一人)
2017/02/12 全球购物
出口公司经理求职简历中的自我评价
2013/10/13 职场文书
精细化工应届生求职信
2013/11/17 职场文书
春季运动会广播稿大全
2014/02/19 职场文书
《少年王冕》教学反思
2014/04/11 职场文书
放射科岗位职责
2015/02/14 职场文书
婚礼嘉宾致辞
2015/07/28 职场文书
幼儿园教师暑期培训心得体会
2016/01/09 职场文书
员工安全责任协议书
2016/03/22 职场文书
干货:企业内部人才推荐奖励方案!
2019/07/09 职场文书
spring cloud gateway中如何读取请求参数
2021/07/15 Java/Android