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实现的简单万年历例子分享
Apr 25 Python
python错误:AttributeError: 'module' object has no attribute 'setdefaultencoding'问题的解决方法
Aug 22 Python
python打开文件并获取文件相关属性的方法
Apr 23 Python
Python编程中的异常处理教程
Aug 21 Python
python爬虫之BeautifulSoup 使用select方法详解
Oct 23 Python
python 通过SSHTunnelForwarder隧道连接redis的方法
Feb 19 Python
Python3+OpenCV2实现图像的几何变换(平移、镜像、缩放、旋转、仿射)
May 13 Python
python 环境搭建 及python-3.4.4的下载和安装过程
Jul 20 Python
python3.7将代码打包成exe程序并添加图标的方法
Oct 11 Python
python opencv 实现对图像边缘扩充
Jan 19 Python
基于Python脚本实现邮件报警功能
May 20 Python
Python3 ffmpeg视频转换工具使用方法解析
Aug 10 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
PHP用strstr()函数阻止垃圾评论(通过判断a标记)
2013/09/28 PHP
php表单文件iframe异步上传实例讲解
2017/07/26 PHP
PHP 二维array转换json的实例讲解
2018/08/21 PHP
基于jQuery实现网页打印功能
2015/12/01 Javascript
Javascript的表单验证长度
2016/03/16 Javascript
特殊日期提示功能的实现方法
2016/06/16 Javascript
总结Javascript中的隐式类型转换
2016/08/24 Javascript
AngularJS表单提交实例详解
2017/02/18 Javascript
js图片放大镜实例讲解(必看篇)
2017/07/17 Javascript
javascript 日期相减-在线教程(附代码)
2017/08/17 Javascript
layer实现关闭弹出层刷新父界面功能详解
2017/11/15 Javascript
解决layui 复选框等内置控件不显示的问题
2018/08/14 Javascript
Nodejs libuv运行原理详解
2019/08/21 NodeJs
python协程用法实例分析
2015/06/04 Python
Django框架下在视图中使用模版的方法
2015/07/16 Python
Python探索之创建二叉树
2017/10/25 Python
点球小游戏python脚本
2018/05/22 Python
python 并发编程 阻塞IO模型原理解析
2019/08/20 Python
python使用openCV遍历文件夹里所有视频文件并保存成图片
2020/01/14 Python
把Anaconda中的环境导入到Pycharm里面的方法步骤
2020/10/30 Python
Python OpenCV中的numpy与图像类型转换操作
2020/12/11 Python
Python爬虫实现selenium处理iframe作用域问题
2021/01/27 Python
cosme官方海外旗舰店:日本最大化妆品和美容产品的综合口碑网站
2017/01/18 全球购物
香港现代设计家具品牌:Ziinlife Furniture
2018/11/13 全球购物
大学生活学习的自我评价
2013/12/03 职场文书
机械制造毕业生求职信
2014/03/03 职场文书
安全目标责任书
2014/07/22 职场文书
完整版商业计划书
2014/09/15 职场文书
离婚协议书怎么写的
2014/12/14 职场文书
离职感谢信怎么写
2015/01/22 职场文书
2016自主招生校长推荐信范文
2015/03/23 职场文书
孔繁森观后感
2015/06/10 职场文书
告知书格式
2015/07/01 职场文书
2019餐饮行业创业计划书!
2019/06/27 职场文书
2019年妇科护士的自我鉴定(3篇)
2019/09/26 职场文书
Java中Quartz高可用定时任务快速入门
2022/04/03 Java/Android