Rob*_*and 118 python decorator python-2.5
在Python 2.5中,有没有办法创建一个装饰类的装饰器?具体来说,我想使用装饰器将一个成员添加到类中,并更改构造函数以获取该成员的值.
寻找类似下面的内容(在'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()
Run Code Online (Sandbox Code Playgroud)
我想我真正想要的是在Python中做一些类似C#接口的方法.我想我需要切换我的范例.
Ste*_*ven 194
除了类装饰器是否是您的问题的正确解决方案的问题:
在Python 2.6及更高版本中,有一些带有@ -syntax的类装饰器,因此您可以编写:
@addID
class Foo:
pass
Run Code Online (Sandbox Code Playgroud)
在旧版本中,您可以采用另一种方式:
class Foo:
pass
Foo = addID(Foo)
Run Code Online (Sandbox Code Playgroud)
但请注意,这与函数装饰器的工作方式相同,并且装饰器应该返回新的(或修改过的原始)类,这不是您在示例中所做的.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
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用适合您的Python版本的语法,如上所述.
但我同意其他人的意见,如果你想覆盖,继承更适合__init__.
Jar*_*die 73
我认为你可能希望考虑一个子类而不是你概述的方法.但是,不知道您的具体情况,YMMV :-)
你在想的是一个元类.__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
Run Code Online (Sandbox Code Playgroud)
替换构造函数可能有点戏剧性,但该语言确实为这种深度内省和动态修改提供了支持.
and*_*oke 17
没有人解释过你可以动态定义类.所以你可以有一个定义(并返回)子类的装饰器:
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
Run Code Online (Sandbox Code Playgroud)
哪个可以在Python 2中使用(Blckknght的注释,它解释了为什么你应该继续在2.6+中这样做),像这样:
class Foo:
pass
FooId = addId(Foo)
Run Code Online (Sandbox Code Playgroud)
在这样的Python 3中(但要小心super()在你的类中使用):
@addId
class Foo:
pass
Run Code Online (Sandbox Code Playgroud)
所以,你可以有你的蛋糕和吃它-继承和装饰!
mpe*_*son 12
这不是一个好习惯,因此没有机制可以做到这一点.实现目标的正确方法是继承.
查看课程文档.
一个小例子:
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)
Run Code Online (Sandbox Code Playgroud)
这种方式Boss拥有一切Employee,还有他自己的__init__方法和自己的成员.
Chr*_*ris 10
我同意继承更适合所提出的问题。
我发现这个问题在装饰类时非常方便,谢谢大家。
这是基于其他答案的另外几个示例,包括继承如何影响 Python 2.7 中的事物(和@wraps,它维护原始函数的文档字符串等):
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...
Run Code Online (Sandbox Code Playgroud)
通常您想向装饰器添加参数:
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()
Run Code Online (Sandbox Code Playgroud)
输出:
@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
Run Code Online (Sandbox Code Playgroud)
我使用了函数装饰器,因为我发现它们更简洁。这是一个用来装饰一个类的类:
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
Run Code Online (Sandbox Code Playgroud)
检查这些括号的更强大的版本,如果装饰类中不存在这些方法,则可以工作:
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
Run Code Online (Sandbox Code Playgroud)
在assert该装饰尚未使用支票没有括号。如果是,则将被装饰的类传递给msg装饰器的参数,从而引发AssertionError.
@decorate_if仅适用decoratorifcondition评估为True。
的getattr,callable测试和@decorate_if使用,这样,如果该装饰不破foo()的方法不会对类正在装修存在。
实际上,这里有一个非常好的类装饰器实现:
https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py
我实际上认为这是一个非常有趣的实现。因为它是它所修饰的类的子类,所以它在检查等方面的行为与该类完全相同isinstance。
__init__它还有一个额外的好处:自定义 django 表单中的语句进行修改或添加并不罕见,self.fields因此最好在所有相关类运行之后self.fields进行更改。__init__
非常聪明。
但是,在您的类中,您实际上希望装饰来改变构造函数,我认为这对于类装饰器来说不是一个好的用例。