代码之家  ›  专栏  ›  技术社区  ›  orokusaki

python-decorator-尝试访问方法的父类

  •  4
  • orokusaki  · 技术社区  · 14 年前

    这不起作用:

    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
    

    当我已经知道代码为什么会被破坏时,把我被破坏的代码放在上面的要点是它传达了我正在尝试做的事情。

    2 回复  |  直到 14 年前
        1
  •  7
  •   Peter Milley    14 年前

    我认为你不能用一个修饰器做你想做的事情(快速编辑:无论如何,用一个方法的修饰器)。当构造方法时调用decorator,即 之前 类被构造。代码不工作的原因是调用decorator时类不存在。

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

        2
  •  2
  •   Jameson Quinn    14 年前

    在python2.6+中,您应该使用类修饰器,而不是使用元类。您可以将函数和类修饰符包装为类的方法,就像这个真实的例子。

    我将这个例子与djcelery一起使用;这个问题的重要方面是“task”方法和行“args,kw=self.marked[klass]。 双关语 [附件]]暗中检查“klass”。 双关语 [attr]在self.marked“中。如果要使用@methodtasks.task而不是@methodtasks.task()作为修饰符,可以删除嵌套的def,并使用set而不是dict作为self.marked。使用self.marked,而不是像另一个答案那样在函数上设置marking属性,这使ClassMethods和StaticMethods能够工作,因为它们使用槽,因此不允许设置任意属性。这样做的缺点是函数decorator必须高于其他decorator,类decorator必须低于其他decorator,这样函数就不会被修改/重新=包装在一个和另一个之间。

    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