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)

相关推荐

  • Python装饰器的应用场景代码总结

    装饰器的应用场景 附加功能 数据的清理或添加: 函数参数类型验证 @require_ints 类似请求前拦截 数据格式转换 将函数返回字典改为 JSON/YAML 类似响应后篡改 为函数提供额外的数据 mock.patch 函数注册 在任务中心注册一个任务 注册一个带信号处理器的函数 不同应用场景下装饰器实现 函数注册表 简单注册表 funcs = [] def register(func): funcs.append(func) return func @register def a(): r

  • python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧装饰器信号与槽详细使用方法与实例

    装饰器信号与槽 所谓装饰器信号与槽,就是通过装饰器的方法来定义信号与槽函数,具体的使用方法如下 @PyQt5.QtCore.pyqtSlot(参数) def on_发送者对象名称_发射信号名称(self,参数): pass 这种方法有效的前提是下面的函数已经执行: QMetaObject.connectSlotsByName(QObject) 在上面的代码中,'发送者对象名称'就是使用setObjectName函数设置的名称,因此自定义槽函数的命名规则也可以看做:on+使用setObjectNa

  • Python @property装饰器原理解析

    这篇文章主要介绍了Python @property装饰器原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.通过@property装饰器,可以直接通过方法名来访问方法,不需要在方法名后添加一对"()"小括号. class Person: def __init__(self, name): self.__name = name @property def say(self): return self.__name xioabai

  • Python装饰器实现方法及应用场景详解

    应用场景: 1.授权(Authorization) 装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint).它们被大量使用于Flask和Django web框架中.这里是一个例子来使用基于装饰器的授权: from functools import wraps # 最新版python引用是 import functools def requires_auth(f): # f 就是我们需要装饰的函数,一看就是不带参数的装饰器 @wraps(f) # 新版python写法 @

  • Python装饰器用法与知识点小结

    本文实例讲述了Python装饰器用法与知识点.分享给大家供大家参考,具体如下: (1)装饰器含参数,被装饰函数不含(含)参数 实例代码如下: import time # 装饰器函数 def wrapper(func): def done(*args,**kwargs): start_time = time.time() func(*args,**kwargs) stop_time = time.time() print('the func run time is %s' % (stop_time

  • python 装饰器功能与用法案例详解

    本文实例讲述了python 装饰器功能与用法.分享给大家供大家参考,具体如下: 1.必备 #### 第一波 #### def foo(): print 'foo' foo #表示是函数 foo() #表示执行foo函数 #### 第二波 #### def foo(): print 'foo' foo = lambda x: x + 1 foo() # 执行下面的lambda表达式,而不再是原来的foo函数,因为函数 foo 被重新定义了 2.需求来了 初创公司有N个业务部门,1个基础平台部门,基

  • 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): pr

  • Python中装饰器兼容加括号和不加括号的写法详解

    使用Django的时候,我发现一个很神奇的装饰器: @login_required, 这是控制一个view的权限的,比如一个视图必须登录才可以访问,可以这样用: @login_required def my_view(request): ... return render(...) 同时,如果要达到这样一种效果:如果用户没有登录,那么就把用户重定向到登录界面,可以这样用: @login_required(login_url='/accounts/login/') def my_view(requ

  • 详解Python中的装饰器、闭包和functools的教程

    装饰器(Decorators) 装饰器是这样一种设计模式:如果一个类希望添加其他类的一些功能,而不希望通过继承或是直接修改源代码实现,那么可以使用装饰器模式.简单来说Python中的装饰器就是指某些函数或其他可调用对象,以函数或类作为可选输入参数,然后返回函数或类的形式.通过这个在Python2.6版本中被新加入的特性可以用来实现装饰器设计模式. 顺便提一句,在继续阅读之前,如果你对Python中的闭包(Closure)概念不清楚,请查看本文结尾后的附录,如果没有闭包的相关概念,很难恰当的理解P

  • Python中的装饰器用法详解

    本文实例讲述了Python中的装饰器用法.分享给大家供大家参考.具体分析如下: 这里还是先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: 复制代码 代码如下: @makebold @makeitalic def say():    return "Hello" 打印出如下的输出: <b><i>Hello<i></b> 你会怎么做?最后给出的答案是: 复制代码 代码如下: def makebold(fn):    

  • Bottle框架中的装饰器类和描述符应用详解

    最近在阅读Python微型Web框架Bottle的源码,发现了Bottle中有一个既是装饰器类又是描述符的有趣实现.刚好这两个点是Python比较的难理解,又混合在一起,让代码有些晦涩难懂.但理解代码之后不由得为Python语言的简洁优美赞叹.所以把相关知识和想法稍微整理,以供分享. 正文 Bottle是Python的一个微型Web框架,所有代码都在一个bottle.py文件中,只依赖标准库实现,兼容Python 2和Python 3,而且最新的稳定版0.12代码也只有3700行左右.虽然小,但

  • 实例讲解Python编程中@property装饰器的用法

    取值和赋值 class Actress(): def __init__(self): self.name = 'TianXin' self.age = 5 类Actress中有两个成员变量name和age.在外部对类的成员变量的操作,主要包括取值和赋值.简单的取值操作是x=object.var,简单的赋值操作是object.var=value. >>> actress = Actress() >>> actress.name #取值操作 'TianXin' >&g

  • Python装饰器实现几类验证功能做法实例

    最近新需求来了,要给系统增加几个资源权限.尽量减少代码的改动和程序的复杂程度.所以还是使用装饰器比较科学 之前用了一些登录验证的现成装饰器模块.然后仿写一些用户管理部分的权限装饰器. 比如下面这种 def permission_required(permission): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.can(permission): abort(40

  • 快速了解Python中的装饰器

    需要理解的一些概念 要理解Python中的装饰器,我觉得还是应该从最基本的概念开始: 装饰器模式:所谓的装饰器模式,可以简单地理解为"在不改变原有内部实现的情况下,为函数或者类添加某种特性".这样我们就可以将一些与业务无关.具有通用性的代码抽象出来,作为装饰器附加到需要这些代码的函数或者类之上.用面向切面编程的思想解释就是"装饰器应该是一个切面". 函数是一等公民:意思就是函数可以被当成普通变量一样使用.在Python中,可以把函数赋值给变量,可以将函数作为其它函数

  • Python如何将装饰器定义为类

    问题 你想使用一个装饰器去包装函数,但是希望返回一个可调用的实例. 你需要让你的装饰器可以同时工作在类定义的内部和外部. 解决方案 为了将装饰器定义成一个实例,你需要确保它实现了 __call__() 和 __get__() 方法. 例如,下面的代码定义了一个类,它在其他函数上放置一个简单的记录层: import types from functools import wraps class Profiled: def __init__(self, func): wraps(func)(self

  • python中的装饰器该如何使用

    目录 1. 需求是怎么来的 装饰器的定义很是抽象,我们来看一个小例子. def foo(): print('in foo()') foo() 这是一个很无聊的函数没错.但是突然有一个更无聊的人,我们称呼他为B君,说我想看看执行这个函数用了多长时间,好吧,那么我们可以这样做: import time def foo(): start = time.time() print('in foo()') time.sleep(2) end = time.time() print(f'used:{end -

随机推荐