简体   繁体   English

Python - 装饰器 - 试图访问方法的父类

[英]Python - decorator - trying to access the parent class of a method

This doesn't work: 这不起作用:

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

Decorators are like the movie Inception; 装饰者就像电影Inception; the more levels in you go, the more confusing they are. 你去的级别越多,它们就越混乱。 I'm trying to access the class that defines a method (at definition time) so that I can set an attribute (or alter an attribute) of the class. 我正在尝试访问定义方法的类(在定义时),以便我可以设置类的属性(或更改属性)。

Version 2 also doesn't work: 版本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

The point of putting my broken code above when I already know why it's broken is that it conveys what I'm trying to do. 当我已经知道为什么它被破坏时,把我破碎的代码放在上面的重点是它传达了我正在尝试做的事情。

I don't think you can do what you want to do with a decorator (quick edit: with a decorator of the method, anyway). 我不认为你可以用装饰器做你想做的事情(快速编辑:使用方法的装饰者,无论如何)。 The decorator gets called when the method gets constructed, which is before the class is constructed. 构造方法时会调用装饰器,这是构造类之前 The reason your code isn't working is because the class doesn't exist when the decorator is called. 您的代码无法工作的原因是因为调用装饰器时类不存在。

jldupont's comment is the way to go: if you want to set an attribute of the class , you should either decorate the class or use a metaclass. jldupont的评论是要走的路:如果你想设置的属性,你应该装饰类或使用元类。

EDIT: okay, having seen your comment, I can think of a two-part solution that might work for you. 编辑:好的,看过你的评论后,我可以想到一个可能适合你的两部分解决方案。 Use a decorator of the method to set an attribute of the method , and then use a metaclass to search for methods with that attribute and set the appropriate attribute of the class : 使用该方法的装饰,设置方法的属性,然后用一元类搜索与该属性的方法和设置的适当的属性:

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)

That's it. 而已。 Use as follows: 使用方法如下:

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'

Honestly, though? 老实说,但是? Use the supported_methods = [...] approach. 使用supported_methods = [...]方法。 Metaclasses are cool, but people who have to maintain your code after you will probably hate you. 元类很酷,但是那些必须在你之后维护代码的人可能会讨厌你。

Rather than use a metaclass, in python 2.6+ you should use a class decorator. 而不是在python 2.6+中使用元类,你应该使用类装饰器。 You can wrap the function and class decorators up as methods of a class, like this real-world example. 您可以将函数和类装饰器包装为类的方法,就像这个真实示例一样。

I use this example with djcelery; 我用djcelery这个例子; the important aspects for this problem are the "task" method and the line "args, kw = self.marked[klass. dict [attr]]" which implicitly checks for "klass. dict [attr] in self.marked". 对于这个问题的一个重要方面是“任务”方法和行“指定参数时,千瓦= self.marked [克拉斯。 字典 [ATTR]]”其隐含检查“克拉斯。 字典 [ATTR]在self.marked”。 If you want to use @methodtasks.task instead of @methodtasks.task() as a decorator, you could remove the nested def and use a set instead of a dict for self.marked. 如果你想使用@ methodtasks.task代替@ methodtasks.task()作为装饰器,你可以删除嵌套的def并使用set而不是dict for self.marked。 The use of self.marked, instead of setting a marking attribute on the function as the other answer did, allows this to work for classmethods and staticmethods which, because they use slots, won't allow setting arbitrary attributes. 使用self.marked,而不是像其他答案那样在函数上设置标记属性,允许这适用于类方法和静态方法,因为它们使用插槽,不允许设置任意属性。 The downside of doing it this way is that the function decorator MUST go above other decorators, and the class decorator MUST go below, so that the functions are not modified / re=wrapped between one and the other. 这样做的缺点是函数装饰器必须高于其他装饰器,类装饰器必须在下面,这样函数不会被修改/重新包装在一个和另一个之间。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM