这不起作用:
def register_method(name=None):
def decorator(method):
# The next line assumes the decorated method is bound (which of course it isn't at this point)
cls = method.im_class
cls.my_attr = 'FOO BAR'
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
return decorator
装饰者就像电影Inception;你去的级别越多,它们就越混乱。我正在尝试访问定义方法的类(在定义时),以便我可以设置类的属性(或更改属性)。
版本2也不起作用:
def register_method(name=None):
def decorator(method):
# The next line assumes the decorated method is bound (of course it isn't bound at this point).
cls = method.__class__ # I don't really understand this.
cls.my_attr = 'FOO BAR'
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
return decorator
当我已经知道为什么它被破坏时,将我破坏的代码置于上面的重点是它传达了我正在尝试做的事情。
答案 0 :(得分:7)
我认为你不能用装饰器做你想做的事情(快速编辑:无论如何使用该方法的装饰器)。构造方法时会调用装饰器,在构造类之前。你的代码不起作用的原因是因为调用装饰器时类不存在。
jldupont的评论是要走的路:如果你想设置类的属性,你应该装饰类或使用元类。
编辑:好的,看过你的评论后,我可以想到一个可能对你有用的两部分解决方案。使用该方法的装饰器设置方法的属性,然后使用元类搜索具有该属性的方法并设置类的适当属性:< / p>
def TaggingDecorator(method):
"Decorate the method with an attribute to let the metaclass know it's there."
method.my_attr = 'FOO BAR'
return method # No need for a wrapper, we haven't changed
# what method actually does; your mileage may vary
class TaggingMetaclass(type):
"Metaclass to check for tags from TaggingDecorator and add them to the class."
def __new__(cls, name, bases, dct):
# Check for tagged members
has_tag = False
for member in dct.itervalues():
if hasattr(member, 'my_attr'):
has_tag = True
break
if has_tag:
# Set the class attribute
dct['my_attr'] = 'FOO BAR'
# Now let 'type' actually allocate the class object and go on with life
return type.__new__(cls, name, bases, dct)
就是这样。使用方法如下:
class Foo(object):
__metaclass__ = TaggingMetaclass
pass
class Baz(Foo):
"It's enough for a base class to have the right metaclass"
@TaggingDecorator
def Bar(self):
pass
>> Baz.my_attr
'FOO BAR'
老实说,但是?使用supported_methods = [...]
方法。元类很酷,但是那些必须在你之后维护代码的人可能会讨厌你。
答案 1 :(得分:2)
不要在python 2.6+中使用元类,而应使用类装饰器。您可以将函数和类装饰器包装为类的方法,就像这个真实示例一样。
我在djcelery中使用这个例子;这个问题的重要方面是“任务”方法和“args,kw = self.marked [klass。 dict [attr]]”这一行隐含地检查“klass。 dict” [attr] in self.marked“。如果你想使用@ methodtasks.task代替@ methodtasks.task()作为装饰器,你可以删除嵌套的def并使用set而不是dict for self.marked。使用self.marked,而不是像其他答案那样在函数上设置标记属性,允许这适用于类方法和静态方法,因为它们使用插槽,不允许设置任意属性。这样做的缺点是函数装饰器必须高于其他装饰器,类装饰器必须在下面,这样函数不会被修改/重新包装在一个和另一个之间。
class DummyClass(object):
"""Just a holder for attributes."""
pass
class MethodTasksHolder(object):
"""Register tasks with class AND method decorators, then use as a dispatcher, like so:
methodtasks = MethodTasksHolder()
@methodtasks.serve_tasks
class C:
@methodtasks.task()
#@other_decorators_come_below
def some_task(self, *args):
pass
@methodtasks.task()
@classmethod
def classmethod_task(self, *args):
pass
def not_a_task(self):
pass
#..later
methodtasks.C.some_task.delay(c_instance,*args) #always treat as unbound
#analagous to c_instance.some_task(*args) (or C.some_task(c_instance,*args))
#...
methodtasks.C.classmethod_task.delay(C,*args) #treat as unbound classmethod!
#analagous to C.classmethod_task(*args)
"""
def __init__(self):
self.marked = {}
def task(self, *args, **kw):
def mark(fun):
self.marked[fun] = (args,kw)
return fun
return mark
def serve_tasks(self, klass):
setattr(self, klass.__name__, DummyClass())
for attr in klass.__dict__:
try:
args, kw = self.marked[klass.__dict__[attr]]
setattr(getattr(self, klass.__name__), attr, task(*args,**kw)(getattr(klass, attr)))
except KeyError:
pass
#reset for next class
self.marked = {}
return klass