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 相关文章推荐
django模型中的字段和model名显示为中文小技巧分享
Nov 18 Python
Python 的描述符 descriptor详解
Feb 27 Python
谈谈如何手动释放Python的内存
Dec 17 Python
python决策树之C4.5算法详解
Dec 20 Python
Python实现PS图像调整之对比度调整功能示例
Jan 26 Python
python进行两个表格对比的方法
Jun 27 Python
python的xpath获取div标签内html内容,实现innerhtml功能的方法
Jan 02 Python
python读写csv文件方法详细总结
Jul 05 Python
Python 迭代,for...in遍历,迭代原理与应用示例
Oct 12 Python
基于Python fminunc 的替代方法
Feb 29 Python
python 截取XML中bndbox的坐标中的图像,另存为jpg的实例
Mar 10 Python
Python爬虫 简单介绍一下Xpath及使用
Apr 26 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批量去除文件UTF8 BOM信息
2013/08/05 PHP
php获取四位字母和数字的随机数的实现方法
2015/01/09 PHP
CI框架整合smarty步骤详解
2016/05/19 PHP
PHP上传图片时判断上传文件是否为可用图片的方法
2016/10/20 PHP
Laravel项目中timeAgo字段语言转换的改善方法示例
2019/09/16 PHP
jQuery插件实现大图全屏图片相册
2015/03/14 Javascript
javascript中select下拉框的用法总结
2016/01/07 Javascript
浅谈DOCTYPE对$(window).height()取值的影响
2016/07/21 Javascript
js控制一个按钮是否可点击(可使用)disabled的实例
2017/02/14 Javascript
使用SVG基本操作API的实例讲解
2017/09/14 Javascript
Angular整合zTree的示例代码
2018/01/24 Javascript
vue 录制视频并压缩视频文件的方法
2018/07/27 Javascript
Nuxt.js开启SSR渲染的教程详解
2018/11/30 Javascript
JS使用iView的Dropdown实现一个右键菜单
2019/05/06 Javascript
webpack4 从零学习常用配置(小结)
2019/05/28 Javascript
JavaScript函数重载操作实例浅析
2020/05/02 Javascript
JavaScript或jQuery 获取option value值方法解析
2020/05/12 jQuery
jQuery 动画与停止动画效果实例详解
2020/05/19 jQuery
python中使用sys模板和logging模块获取行号和函数名的方法
2014/04/15 Python
Python实现视频下载功能
2017/03/14 Python
Python探索之修改Python搜索路径
2017/10/25 Python
快速排序的四种python实现(推荐)
2019/04/03 Python
python 实现将多条曲线画在一幅图上的方法
2019/07/07 Python
twilio python自动拨打电话,播放自定义mp3音频的方法
2019/08/08 Python
Python下划线5种含义代码实例解析
2020/07/10 Python
python从Oracle读取数据生成图表
2020/10/14 Python
CSS3 特效范例整理
2011/08/22 HTML / CSS
李宁官方网店:中国运动品牌
2017/11/02 全球购物
英国折扣高尔夫商店:Discount Golf Store
2019/11/19 全球购物
泰海淘:泰国king Power王权免税集团旗下跨境海淘综合型电商
2020/07/26 全球购物
中学生寄语大全
2014/04/03 职场文书
个人自荐书范文
2015/03/09 职场文书
优秀范文:《但愿人长久》教学反思3篇
2019/10/24 职场文书
Python基于Tkinter开发一个爬取B站直播弹幕的工具
2021/05/06 Python
Go语言 详解net的tcp服务
2022/04/14 Golang
Android RecyclerView实现九宫格效果
2022/06/28 Java/Android