【问题标题】:Python dynamically add decorator to class' methods by decorating classPython通过装饰类为类的方法动态添加装饰器
【发布时间】:2011-07-25 19:04:45
【问题描述】:

说我有课:

class x:

    def first_x_method(self):
        print 'doing first_x_method stuff...'

    def second_x_method(self):
        print 'doing second_x_method stuff...'

还有这个装饰器

class logger:
    @staticmethod
    def log(func):
        def wrapped(*args, **kwargs):
            try:
                print "Entering: [%s] with parameters %s" % (func.__name__, args)
                try:
                    return func(*args, **kwargs)
                except Exception, e:
                    print 'Exception in %s : %s' % (func.__name__, e)
            finally:
                print "Exiting: [%s]" % func.__name__
        return wrapped

我将如何编写另一个装饰器 otherdecorator 以便:

@otherdecorator(logger.log)
class x:

    def first_x_method(self):
        print 'doing x_method stuff...'

    def first_x_method(self):
        print 'doing x_method stuff...'

一样
class x:
      @logger.log
      def first_x_method(self):
          print 'doing first_x_method stuff...'

      @logger.log
      def second_x_method(self):
        print 'doing second_x_method stuff...'

或者实际上替换

@otherdecorator(logger.log)
class x:

@otherdecorator 
class x:

其中 otherdecorator 包含所有功能 (我不是蟒蛇人所以要温柔)

【问题讨论】:

  • 你用的是什么版本的 Python?
  • 2.6 和 Iron Python (clr 4.0/dlr)

标签: python aop decorator


【解决方案1】:

除非有明确的理由使用类作为装饰器,否则我认为使用函数来定义装饰器通常更容易。

这是创建类装饰器trace 的一种方法,它使用log 装饰器装饰类的所有方法:

import inspect


def log(func):
    def wrapped(*args, **kwargs):
        try:
            print("Entering: [%s] with parameters %s" % (func.__name__, args))
            try:
                return func(*args, **kwargs)
            except Exception as e:
                print('Exception in %s : %s' % (func.__name__, e))
        finally:
            print("Exiting: [%s]" % func.__name__)
    return wrapped


def trace(cls):
    # https://stackoverflow.com/a/17019983/190597 (jamylak)
    for name, m in inspect.getmembers(cls, lambda x: inspect.isfunction(x) or inspect.ismethod(x)):
        setattr(cls, name, log(m))

    return cls


@trace
class X(object):
    def first_x_method(self):
        print('doing first_x_method stuff...')

    def second_x_method(self):
        print('doing second_x_method stuff...')


x = X()
x.first_x_method()
x.second_x_method()

产量:

Entering: [first_x_method] with parameters (<__main__.X object at 0x7f19e6ae2e80>,)
doing first_x_method stuff...
Exiting: [first_x_method]
Entering: [second_x_method] with parameters (<__main__.X object at 0x7f19e6ae2e80>,)
doing second_x_method stuff...
Exiting: [second_x_method]

【讨论】:

  • 太棒了。非常感谢。
  • 所以大概我可以在运行时添加装饰器,并在要隔离的类型上添加一个 settattr?
  • @Preet Sangha:是的,我认为不会有任何问题——除了在应用装饰器之前创建的类的实例不会被修改。
  • @unutbu,我的Python版本是3.6.8,运行结果是:doing first_x_method stuff... doing second_x_method stuff...
  • @Tengerye:上面的代码现在已经针对 Python3 进行了更新。主要区别在于 Python3 中的 method is just a plain function 因此谓词需要更改为 inspect.isfunction 而不是 inspect.ismethod
【解决方案2】:

这是作为类实现的 trace 装饰器的一个版本,它允许要求的其他用例:传入函数来装饰装饰类的所有成员函数。

import inspect


def log(func):
    def wrapped(*args, **kwargs):
        try:
            print "Entering: [%s] with parameters %s" % (func.__name__, args)
            try:
                return func(*args, **kwargs)
            except Exception, e:
                print 'Exception in %s : %s' % (func.__name__, e)
        finally:
            print "Exiting: [%s]" % func.__name__
    return wrapped


class trace(object):

    def __init__(self, f):
        self.f = f

    def __call__(self, cls):
        for name, m in inspect.getmembers(cls, inspect.ismethod):
            setattr(cls, name, self.f(m))
        return cls


@trace(log)
class X(object):

    def first_x_method(self):
        print 'doing first_x_method stuff...'

    def second_x_method(self):
        print 'doing second_x_method stuff...'

x = X()
x.first_x_method()
x.second_x_method()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-14
    • 2013-04-04
    • 2012-04-11
    • 1970-01-01
    • 2020-01-11
    • 2012-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多