Python类装饰器

在Python 2.5中,是否有办法创build一个装饰器来装饰一个类? 具体来说,我想使用装饰器将成员添加到类,并更改构造函数为该成员的值。

寻找像下面这样的东西(在'class Foo:'上有一个语法错误:

def getId(self): return self.__id class addID(original_class): def __init__(self, id, *args, **kws): self.__id = id self.getId = getId original_class.__init__(self, *args, **kws) @addID class Foo: def __init__(self, value1): self.value1 = value1 if __name__ == '__main__': foo1 = Foo(5,1) print foo1.value1, foo1.getId() foo2 = Foo(15,2) print foo2.value1, foo2.getId() 

谢谢,罗布

编辑:重读这个问题,我想我真的是一种像Python中的C#接口一样的方法。 我需要改变我的想法。

我将第二个概念,你可能希望考虑一个子类,而不是你所概述的方法。 但是,不知道你的具体情况,YMMV 🙂

你在想什么是元类。 元类中的__new__函数传递了类的全部build议定义,然后可以在类创build之前重写。 你可以在那个时候把构造函数转换成一个新的。

例:

 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 

replace构造函数可能有点戏剧性,但是这种语言的确提供了对这种深度的内省和dynamic修改的支持。

除了类装饰器是否正确解决您的问题之外,

在Python 2.6及更高版本中,有类装饰器和@ -syntax,所以你可以写:

 @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__ ,那么inheritance是更合适的。

这不是一个好的做法,也没有这样做的机制。 完成你想要的东西的正确方法是inheritance。

看看类文档 。

一个小例子:

 class Employee(object): def __init__(self, age, sex, siblings=0): self.age = age self.sex = sex self.siblings = siblings def born_on(self): today = datetime.date.today() return today - datetime.timedelta(days=self.age*365) class Boss(Employee): def __init__(self, age, sex, siblings=0, bonus=0): self.bonus = bonus Employee.__init__(self, age, sex, siblings) 

这样老板拥有一切Employee ,也有他自己的__init__方法和自己的成员。

没有人解释说你可以dynamic定义类。 所以你可以有一个装饰器来定义(和返回)一个子类:

 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) 

和在这样的python3(但要注意在你的类中使用super() ):

 @addId class Foo: pass 

所以你可以有你的蛋糕吃它 – inheritance装饰者!

实际上这里有一个很好的类装饰器实现:

https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py

我其实觉得这是一个非常有趣的实现。 因为它的子类是它所装饰的类,所以它将像isinstance检查一样在这个类中performance得完全一样。

它还有一个额外的好处:自定义django表单中的__init__语句对self.fields进行修改或添加的情况并不less见,因此, 所有类的__init__运行完毕后更改self.fields会更好。

非常聪明。

然而,在你的类中,你实际上希望装饰改变构造函数,我认为这不是一个类装饰器的好用例。

我同意inheritance是一个更适合所提出的问题。

我发现这个问题真的很方便,但装饰类,谢谢大家。 这里有另外几个例子,基于其他的答案,包括inheritance如何影响Python 2.7中的东西(和@wraps ,它维护原始函数的文档string等):

 def dec(klass): old_foo = klass.foo @wraps(klass.foo) def decorated_foo(self, *args ,**kwargs): print('@decorator pre %s' % msg) old_foo(self, *args, **kwargs) print('@decorator post %s' % msg) klass.foo = decorated_foo return klass @dec # no parentheses class Foo... 

通常你想添加参数到你的装饰器:

 from functools import wraps def dec(msg='default'): def decorator(klass): old_foo = klass.foo @wraps(klass.foo) def decorated_foo(self, *args ,**kwargs): print('@decorator pre %s' % msg) old_foo(self, *args, **kwargs) print('@decorator post %s' % msg) klass.foo = decorated_foo return klass return decorator @dec('foo decorator') # you must add parentheses now, even if they're empty class Foo(object): def foo(self, *args, **kwargs): print('foo.foo()') @dec('subfoo decorator') class SubFoo(Foo): def foo(self, *args, **kwargs): print('subfoo.foo() pre') super(SubFoo, self).foo(*args, **kwargs) print('subfoo.foo() post') @dec('subsubfoo decorator') class SubSubFoo(SubFoo): def foo(self, *args, **kwargs): print('subsubfoo.foo() pre') super(SubSubFoo, self).foo(*args, **kwargs) print('subsubfoo.foo() post') SubSubFoo().foo() 

输出:

 @decorator pre subsubfoo decorator subsubfoo.foo() pre @decorator pre subfoo decorator subfoo.foo() pre @decorator pre foo decorator foo.foo() @decorator post foo decorator subfoo.foo() post @decorator post subfoo decorator subsubfoo.foo() post @decorator post subsubfoo decorator 

我用过一个函数装饰器,因为我觉得它们更简洁。 这里有一个类来装饰一个类:

 class Dec(object): def __init__(self, msg): self.msg = msg def __call__(self, klass): old_foo = klass.foo msg = self.msg def decorated_foo(self, *args, **kwargs): print('@decorator pre %s' % msg) old_foo(self, *args, **kwargs) print('@decorator post %s' % msg) klass.foo = decorated_foo return klass 

一个更强大的版本,用于检查这些括号,如果装饰类中不存在这些方法,则工作:

 from inspect import isclass def decorate_if(condition, decorator): return decorator if condition else lambda x: x def dec(msg): # Only use if your decorator's first parameter is never a class assert not isclass(msg) def decorator(klass): old_foo = getattr(klass, 'foo', None) @decorate_if(old_foo, wraps(klass.foo)) def decorated_foo(self, *args ,**kwargs): print('@decorator pre %s' % msg) if callable(old_foo): old_foo(self, *args, **kwargs) print('@decorator post %s' % msg) klass.foo = decorated_foo return klass return decorator 

assert检查装饰器没有使用括号。 如果有,则正在装饰的类传递给装饰器的msg参数,这引发了一个AssertionError

如果condition评估为True @decorate_if只应用decorator

getattrcallable test和@decorate_if被使用,所以如果被装饰的类中不存在foo()方法,装饰器不会中断。