【问题标题】:Python - decorator - trying to access the parent class of a methodPython - 装饰器 - 试图访问方法的父类
【发布时间】:2010-10-07 20:05:02
【问题描述】:

这不起作用:

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

当我已经知道它为什么会损坏时,将损坏的代码放在上面的目的是它传达了我想要做的事情。

【问题讨论】:

  • 和制作一个元类没有帮助?

标签: python decorator


【解决方案1】:

我不认为你可以用装饰器做你想做的事(快速编辑:无论如何,用方法的装饰器)。构造方法时调用装饰器,也就是构造类之前。您的代码不起作用的原因是调用装饰器时该类不存在。

jldupont 的评论是要走的路:如果你想设置 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)

就是这样。使用如下:

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

【讨论】:

  • 谢谢。我现在要开始重新长出我在过去一个小时里失去的头发:) 不过我该怎么做呢?我不了解元类,但我也不明白装饰类如何帮助我正在做的事情。澄清一下,我需要能够在 self.supports_method(method_name_string) 类的实例上运行一个方法,以查看这些方法是否受支持。我试图让它“酷”,尽管没有子类必须在每个类上声明 supported_methods = ['method_one', 'method_two'] 属性。
  • 优秀 - 我刚刚花了最后 30 分钟阅读 IBM 和其他关于元类的 SO 问题,我很高兴我做到了。我得出的结论是(基于在 SO 答案中使用 func.is_hook)我需要标记包含在我的装饰器中的方法(除非我想相信纯元魔法来通过其他方法弄清楚我想要什么公约)。当我意识到我还剩下多少“弄清楚”这一天时,我正要上吊。这就是我刷新页面的内容:) 你刚刚拯救了我剩下的头发。谢谢彼得。
  • 我可能会相信你关于不使用元编程的智慧,但直到我先这样做:)(如果我不这样做,我会忘记怎么做)。
【解决方案2】:

在 python 2.6+ 中,您应该使用类装饰器,而不是使用元类。您可以将函数和类装饰器包装为类的方法,就像这个真实世界的示例一样。

我将这个例子与 djcelery 一起使用;这个问题的重要方面是 "task" 方法和 "args, kw = self.marked[klass.dict[attr]]" 行,它隐式检查 "klass.dict [attr] in self.marked”。如果你想使用 @methodtasks.task 而不是 @methodtasks.task() 作为装饰器,你可以删除嵌套的 def 并使用 set 而不是 dict 来表示 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

【讨论】:

    猜你喜欢
    • 2019-08-07
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    • 1970-01-01
    • 2018-07-14
    • 2011-06-26
    • 2018-11-18
    • 2012-02-09
    相关资源
    最近更新 更多