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常见文件操作的函数示例代码
Nov 15 Python
使用Python开发windows GUI程序入门实例
Oct 23 Python
在Python中操作字符串之rstrip()方法的使用
May 19 Python
Python3指定路径寻找符合匹配模式文件
May 22 Python
python利用拉链法实现字典方法示例
Mar 25 Python
Python实现判断字符串中包含某个字符的判断函数示例
Jan 08 Python
python 异或加密字符串的实例
Oct 14 Python
python将pandas datarame保存为txt文件的实例
Feb 12 Python
python对指定字符串逆序的6种方法(小结)
Apr 02 Python
python 通过文件夹导入包的操作
Jun 01 Python
Python破解极验滑动验证码详细步骤
May 21 Python
Python 绘制多因子柱状图
May 11 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和js交互一例-PHP教程,PHP应用
2007/01/03 PHP
php数组去重的函数代码
2013/02/03 PHP
PHP开发框架kohana3 自定义路由设置示例
2014/07/14 PHP
CI框架学习笔记(二) -入口文件index.php
2014/10/27 PHP
phpinfo()中Loaded Configuration File(none)的解决方法
2017/01/16 PHP
PHP基于mcript扩展实现对称加密功能示例
2019/02/21 PHP
Javascript 判断客户端浏览器类型代码
2010/03/01 Javascript
映彩衣的js随笔(js图片切换效果)
2011/07/31 Javascript
超链接的禁用属性Disabled使用示例
2014/07/31 Javascript
基于NodeJS的前后端分离的思考与实践(五)多终端适配
2014/09/26 NodeJs
jQuery元素的隐藏与显示实例
2015/01/20 Javascript
javaScript的函数对象的声明详解
2015/02/06 Javascript
谈谈我对JavaScript DOM事件的理解
2015/12/18 Javascript
去除html代码里面的script正则方法
2016/05/19 Javascript
JavaScript 节流函数 Throttle 详解
2016/07/04 Javascript
再谈javascript常见错误及解决方法
2016/09/16 Javascript
JS实现浏览上传文件的代码
2017/08/23 Javascript
详解VUE Element-UI多级菜单动态渲染的组件
2019/04/25 Javascript
JS实现图片懒加载(lazyload)过程详解
2020/04/02 Javascript
Threejs实现滴滴官网首页地球动画功能
2020/07/13 Javascript
vue项目中使用多选框的实例代码
2020/07/22 Javascript
python遍历文件夹并删除特定格式文件的示例
2014/03/05 Python
django实现同一个ip十分钟内只能注册一次的实例
2017/11/03 Python
python实现mysql的读写分离及负载均衡
2018/02/04 Python
详解Python中pandas的安装操作说明(傻瓜版)
2019/04/08 Python
UGG澳洲官网:UGG Australia
2018/04/26 全球购物
PHP中如何创建和修改数组
2012/05/02 面试题
医学毕业生自荐信
2013/10/11 职场文书
酒店个人培训自我鉴定
2013/12/11 职场文书
《谁的本领大》教后反思
2014/04/25 职场文书
普通党员个人整改措施
2014/10/27 职场文书
MySQL官方导出工具mysqlpump的使用
2021/05/21 MySQL
Spring实现内置监听器
2021/07/09 Java/Android
Nginx使用Lua模块实现WAF的原理解析
2021/09/04 Servers
Python之Matplotlib绘制热力图和面积图
2022/04/13 Python
MySQL如何使备份得数据保持一致
2022/05/02 MySQL