Python 装饰子类中的所有方法,并提供重写方式

7 投票
1 回答
2879 浏览
提问于 2025-04-17 06:55

我正在寻找一种方法来减少重复的装饰器代码。我们有很多类都使用了 @decorate 这个装饰器。例如:

class MyClass(Base):
     @decorate
     def fun1(self):
         pass
     @decorate
     def fun2(self):
         pass
     def fun3(self):
         pass

我想默认情况下这个装饰器是存在的,除非有人特别说明不需要。


我用这段代码来自动添加装饰器:

from functools import wraps

def myDecorator(func):
    @wraps(func)
    def decorator(self, *args, **kwargs):
        try:
            print 'enter'
            ret = func(self, *args, **kwargs)
            print 'leave'
        except:
            print 'exception'
            ret = None

        return ret

    return decorator

class TestDecorateAllMeta(type):
    def __new__(cls, name, bases, local):
        for attr in local:
            value = local[attr]
            if callable(value):
                local[attr] = myDecorator(value)
        return type.__new__(cls, name, bases, local)

class TestClass(object):
    __metaclass__ = TestDecorateAllMeta

    def test_print2(self, val):
        print val

    def test_print(self, val):
        print val

c = TestClass()
c.test_print1("print 1")
c.test_print2("print 2")

我想问的问题是:

  1. 有没有更好的方法来实现自动添加装饰器?
  2. 我该如何进行覆盖?

理想情况下,我最终的解决方案应该是这样的:

class TestClass(object):
    __metaclass__ = TestDecorateAllMeta

    def autowrap(self):
        print("Auto wrap")

    @dont_decorate
    def test_dont_decorate(self, val):
        print val

编辑

为了回应下面的一个评论,由于类是可调用的,所以不需要这样做:

if callable(value):

应该改成:

if isinstance(value,types.FunctionType) 

1 个回答

4

与其让我的类的用户自己指定一个 __metaclass__ 属性,不如让他们直接从我定义的基类继承,这样就不需要让用户接触那些复杂的东西了。

除此之外,其他部分看起来不错。你的 @dont_decorate 函数装饰器可以通过在原始函数上设置一个属性来实现,这样你的类装饰器就可以检测到这个属性,如果存在就跳过装饰。

def dont_decorate(func):
    func._dont_decorate = True
    return func

然后在你的 metaclass 中,原本有一行 if callable(value):,你只需要改成:

if callable(value) and not hasttr(value, "_dont_decorate"):

顺便提一下,类是可以被调用的,所以如果你不想让内部类被装饰,最好用 isinstance() 来检查函数,而不是用 callable()

如果你对更明确的替代方案感兴趣,可以看看 这个最近的问题,里面有人想用类装饰器做类似的事情。不幸的是,这要复杂得多,因为方法在装饰器看到它们的时候已经被包装过了。

撰写回答