2010-10-07 53 views
4

的这不起作用:Python的 - 装饰 - 试图访问父类的方法

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 

装饰都像电影盗梦空间;你走向的层次越多,他们就越容易混淆。我试图访问定义方法的类(定义时),以便我可以设置类的属性(或更改属性)。

版本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

和制作一个元类不会帮助? – jldupont 2010-10-07 20:06:41

回答

7

我不认为你可以做你想做一个装饰做什么(快速编辑:根据该方法的一个装饰,反正)。构造器在构造该方法时调用,即之前构造该类。你的代码不工作的原因是因为调用装饰器时该类不存在。

jldupont的评论是要走的路:如果要设定的类的属性,您应该装饰类或使用元类。

编辑:好的,在看到你的评论,我能想到的两部分的解决方案,可能会为你工作。使用该方法的装饰,设置方法的属性,然后用一元类搜索与该属性的方法和设置类的相应属性:

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 = [...]的方法。元类很酷,但在你之后必须维护你的代码的人可能会恨你。

+0

谢谢。我现在开始在最后一个小时内重新修复我失去的头发:)但是,我会如何去做呢?我不了解元类,但我也不明白装饰课堂如何帮助我做什么。为了澄清,我需要能够在'self.supports_method(method_name_string)'类的实例上运行一个方法来查看这些方法是否受支持。尽管没有子分类器必须在每个类中声明'supported_methods = ['method_one','method_two']'属性,但我试图让它变得“很酷”。 – orokusaki 2010-10-07 20:31:56

+0

@orokusaki:看到编辑。 – 2010-10-07 20:55:16

+0

优秀 - 我只花了最后30分钟阅读IBM和其他有关元类的问题,我很高兴自己能做到。我得出了结论(基于在SO答案中使用'func.is_hook'),我需要标记包含在我的装饰器中的方法(除非我想相信纯粹的元魔法来找出我想要的东西通过其他公约)。在我意识到我还有多少“计算出”我今天已经离开之后,我正要挂上自己。这就是我刷新页面的原因:)你只剩下我剩下的头发了。谢谢彼得。 – orokusaki 2010-10-07 21:04:18

2

除了使用元类,在Python 2.6+,你应该使用类装饰。您可以将函数和类装饰器作为类的方法包装起来,就像这个真实世界的例子一样。

我用djcelery这个例子;这个问题的重要方面是“任务”方法和“args,kw = self.marked [klass。dict [attr]]”这一行隐含地检查“klass。dict [attr] in self。标有”。如果您想使用@ methodtasks.task而不是@ methodtasks.task()作为装饰器,则可以删除嵌套def并使用set而不是字典来标记self.marked。采用self.marked的,而不是因为对方的回答做了功能设置标记属性,这可以为classmethods和staticmethods这工作,因为他们使用的槽,将不允许设置任意属性。这样做的缺点是,该函数必须装饰上面去,其它装饰,并且类装饰必须去的下方,从而使功能不被修改/重新=一个和另一个之间的缠绕。

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