使用类定义装饰器来装饰Python中的类

26

如何通过将修饰器定义为类来装饰一个类的简单示例是什么?

我正在尝试使用类而不是函数来实现 Python 2.6 中使用 PEP 3129 实现的内容,就像 Bruce Eckel 在 这里 解释的那样。

以下代码可以工作:

class Decorator(object):
    def __init__(self, arg):
        self.arg = arg

    def __call__(self, cls):
        def wrappedClass(*args):
            return cls(*args)
        return type("TestClass", (cls,), dict(newMethod=self.newMethod, classattr=self.arg))

    def newMethod(self, value):
        return value * 2

@Decorator("decorated class")
class TestClass(object):
    def __init__(self):
        self.name = "TestClass"
        print "init %s"%self.name

    def TestMethodInTestClass(self):
        print "test method in test class"

    def newMethod(self, value):
        return value * 3

除此之外,在上述代码中,wrappedClass 不是一个类,而是一个被操作后返回一个类类型的函数。我想编写相同的可调用函数如下:
def __call__(self, cls):
        class wrappedClass(cls):
            def __init__(self):
                ... some code here ...
        return wrappedClass

我该如何做到这一点?

我不完全确定“……这里放一些代码……”需要包含什么。


你自己尝试过你发布的代码了吗?它应该能够工作。 - Sven Marnach
第一部分使用函数确实有效。但是,我如何编写wrappedClass作为真正的类呢? - tsps
1
你的装饰器应该做什么?如果不知道这个代码的作用,我就无法告诉你在“一些代码”中需要编写什么代码。 - Sven Marnach
我想通过使用类来实现函数所能实现的功能。我知道这是可以做到的,但找不到任何示例来说明这一点。 - tsps
我不明白。你第二段代码应该可以直接运行。在“some code here”处,你可以放置任何代码。但是我怎么知道这些代码的作用呢?如果你不想覆盖__init __(),那就不要覆盖。如果你确实想覆盖它,显然你想以某种方式改变它的行为。我的问题是:你想以什么方式改变它的行为? - Sven Marnach
装饰器应该修改TestClass中的“newMethod”方法,将其转换为仅将值加倍的方法。换句话说,它必须替换TestClass中的方法。在声明__init__(self)后不久,我就会感到困惑,并且会因为装饰器对自身、被装饰类(cls)和包装类的self的引用而感到困惑。希望这有助于消除混淆。 - tsps
2个回答

27

如果您想覆盖 new_method(),只需这样做:

class Decorator(object):
    def __init__(self, arg):
        self.arg = arg
    def __call__(self, cls):
        class Wrapped(cls):
            classattr = self.arg
            def new_method(self, value):
                return value * 2
        return Wrapped

@Decorator("decorated class")
class TestClass(object):
    def new_method(self, value):
        return value * 3

如果您不想更改__init__(),则无需覆盖它。


1
有没有任何情况下我们应该使用类装饰器而不是继承类? - han shih

8

在此之后,普通类NormalClass变成了一个ClassWrapper实例

def decorator(decor_arg):

    class ClassWrapper:
        def __init__(self, cls):
            self.other_class = cls

        def __call__(self,*cls_ars):
            other = self.other_class(*cls_ars)
            other.field += decor_arg 
            return other

    return ClassWrapper

@decorator(" is now decorated.")
class NormalClass:
    def __init__(self, name):
        self.field = name

    def __repr__(self):
        return str(self.field)

测试:

if __name__ == "__main__":

    A = NormalClass('A');
    B = NormalClass('B');

    print A
    print B
    print NormalClass.__class__

输出:

A is now decorated. <br>
B is now decorated. <br>
\__main__.classWrapper

3
你忘记在__call__方法内返回'other'变量。 - Melvic Ybanez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接