如何装饰类中的所有方法?可以只装饰类吗?

1 投票
3 回答
4397 浏览
提问于 2025-04-18 03:46

我有几个类,它们的方法名字是一样的,但实现的方式不同。我想给某些类里的所有方法加上装饰器,但其他类就不需要。我考虑过用继承的方式,但有些类里面有些方法是不需要加装饰器的。问题是我不想一个一个方法去加装饰器,有些类需要用同一个装饰器。有没有什么办法可以解决这个问题呢?

3 个回答

0

在某个时候,你需要明确哪些内容需要被包裹起来,哪些不需要。

如果我理解得没错,我觉得你可以这样做:

def wrapper(func):
    def inner(*args, **kwargs):
        print "%s was called" func.__name__
        return func(*args, **kwargs)
    return inner

class A(object):
    def foo(self):
        print "foo called"
    def bar(self):
        print "BAR CALLED"

class B(A):
    @wrapper
    def foo(self):
        super(B, self).foo()

class C(A):
    @wrapper
    def bar(self):
        super(C, self).bar()

Stick = A()
Dave = B()
Jupiter = C()

Jupiter.foo() #prints "foo called"
Jupiter.bar() #prints "bar wrapped" and "BAR CALLED"
1

我相信有几种方法可以解决这个问题,但主要的选择有:

  • 创建一个自定义的元类,在这个元类的 __new__ 方法中,遍历属性字典,识别出方法并给它们加上装饰器。想了解更多,可以看看这个链接:http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/。缺点是:这可能比我们想要的要复杂。
  • 在普通类的 __init__ 方法中做同样的事情。缺点是:这样只会给实例方法加装饰器,而不会给类方法或静态方法加装饰器,而且每次创建新实例时都会运行,速度比较慢。
  • 在类外部进行:

    class Foo(object):
      def bar(self):
        print 'bar'
    
    for name, ref in vars(Foo):
      if callable(ref): ...
    

    缺点是:你只有一次机会做对,必须在导入时完成。子类不会被修改。

  • 在类级别的装饰器中进行。缺点和在类外部做是一样的(我认为)。

2

你可以给所有需要加上某个前缀的方法开始时加上这个前缀,然后用类似下面的方式:

class Xobject(object):

    def __init__(self, decorator):
        for method_name in dir(self):
            if method_name.startswith("dec_"):
                attr = getattr(self, method_name)
                wrapped = decorator(attr)
                setattr(self, method_name, wrapped)

    def dec_me_1(self):
        print("In dec_me1")
        return 0

    def dec_me_2(self):
        print("In dec_me2")
        return 1


def decorator(func):

    def wrapped(*args):
        print("TEST")
        return func(*args)

    return wrapped


x = Xobject(decorator)

x.dec_me_1()
x.dec_me_2()

更新:

你可以通过下面的函数来装饰类。在使用Python的时候,你应该知道,Python中的class也是一种对象,所以你可以对它进行修改,并把它传递给其他函数。

def decorator(func):

    def wrapped(*args):
        print("TEST")
        return func(*args)

    return wrapped


def decorate_object(p_object, decorator):
    for method_name in dir(p_object):
        if method_name.startswith("dec_"):
            attr = getattr(p_object, method_name)
            wrapped = decorator(attr)
            setattr(p_object, method_name, wrapped)

decorate_object(Xobject, decorator)

x = Xobject()

x.dec_me_1()
x.dec_me_2()

另外,你也可以用同样的方式来装饰已经实例化的对象:

x = Xobject()

x.dec_me_1()
x.dec_me_2()

decorate_object(x, decorator)

x.dec_me_1()
x.dec_me_2()

撰写回答