如何装饰一个类?
我该如何创建一个可以应用于类的装饰器呢?
具体来说,我想用一个叫做 addID
的装饰器,给一个类添加一个成员 __id
,并且修改构造函数 __init__
,让它可以接收一个 id
参数来为这个成员赋值。
def getId(self): return self.__id
classdecorator addID(cls):
def __init__(self, id, *args, **kws):
self.__id = id
self.getId = getId
cls.__init__(self, *args, **kws)
@addID
class Foo:
def __init__(self, value1):
self.value1 = value1
上面的内容应该等同于:
class Foo:
def __init__(self, id, value1):
self.__id = id
self.value1 = value1
def getId(self): return self.__id
8 个回答
35
没有人解释过你可以动态地定义类。也就是说,你可以创建一个装饰器,它可以定义(并返回)一个子类:
def addId(cls):
class AddId(cls):
def __init__(self, id, *args, **kargs):
super(AddId, self).__init__(*args, **kargs)
self.__id = id
def getId(self):
return self.__id
return AddId
在Python 2中可以这样使用(Blckknght的评论解释了为什么你应该在2.6+版本中继续这样做):
class Foo:
pass
FooId = addId(Foo)
在Python 3中可以这样使用(但要注意在你的类中使用super()
):
@addId
class Foo:
pass
所以你可以两全其美,既能享受继承,又能使用装饰器!
244
除了讨论类装饰器是否是解决你问题的正确方法之外:
在Python 2.6及更高版本中,有一种使用@符号的类装饰器,你可以这样写:
@addID
class Foo:
pass
在旧版本中,你可以用另一种方式来实现:
class Foo:
pass
Foo = addID(Foo)
不过要注意,这种方式和函数装饰器的工作原理是一样的,装饰器应该返回一个新的(或修改过的原始)类,而在你的例子中并没有做到这一点。addID装饰器应该是这样的:
def addID(original_class):
orig_init = original_class.__init__
# Make copy of original __init__, so we can call it without recursion
def __init__(self, id, *args, **kws):
self.__id = id
self.getId = getId
orig_init(self, *args, **kws) # Call the original __init__
original_class.__init__ = __init__ # Set the class' __init__ to the new one
return original_class
然后你可以根据你使用的Python版本,使用上面描述的正确语法。
但我同意其他人的看法,如果你想重写__init__
方法,继承会更合适。
87
我同意你可能想考虑使用子类,而不是你提到的方法。不过,由于我不知道你具体的情况,结果可能会有所不同哦 :-)
你想到的是元类。元类中的 __new__
函数会接收到完整的类定义,然后可以在类创建之前对其进行修改。这个时候,你可以把构造函数换成一个新的。
举个例子:
def substitute_init(self, id, *args, **kwargs):
pass
class FooMeta(type):
def __new__(cls, name, bases, attrs):
attrs['__init__'] = substitute_init
return super(FooMeta, cls).__new__(cls, name, bases, attrs)
class Foo(object):
__metaclass__ = FooMeta
def __init__(self, value1):
pass
替换构造函数可能有点夸张,但这个语言确实支持这种深层次的检查和动态修改。