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
。
getattr
, callable
test和@decorate_if
被使用,所以如果被装饰的类中不存在foo()
方法,装饰器不会中断。