有没有办法将装饰器应用于需要有关类的信息的Python方法?

时间:2012-10-08 09:09:00

标签: python class methods decorator python-decorators

当你装饰一个方法时,它还没有绑定到类,因此还没有im_class属性。我正在寻找一种方法来获取装饰器内部类的信息。我试过这个:

import types

def decorator(method):

    def set_signal(self, name, value):
        print name
        if name == 'im_class':
            print "I got the class"

    method.__setattr__ = types.MethodType(set_signal, method)

    return method


class Test(object):
    @decorator
    def bar(self, foo):
        print foo

但它没有打印任何东西。

我可以想象这样做:

class Test(object):
    @decorator(klass=Test)
    def bar(self, foo):
        print foo

但如果我能避免它,它就会成为我的一天。

4 个答案:

答案 0 :(得分:3)

__setattr__仅在明确object.attribute =作业时调用;构建类不使用属性赋值,而是构建字典(Test.__dict__)。

要访问该课程,您有几个不同的选择:

  1. 改为使用类装饰器;在构建它之后它将被传递给完成的类,你可以通过在类中替换它们(装饰)来装饰该类的各个方法。您可以使用函数装饰器和类装饰器的组合来标记要装饰的方法:

    def methoddecoratormarker(func):
        func._decorate_me = True
        return func
    
    def realmethoddecorator(func):
        # do something with func. 
        # Note: it is still an unbound function here, not a method!
        return func
    
    def classdecorator(klass):
        for name, item in klass.__dict__.iteritems():
            if getattr(item, '_decorate_me', False):
                klass.__dict__[name] = realmethoddecorator(item)
    

    当然,您可以使用元类而不是类装饰器来实现相同目的。

  2. 作弊,并使用sys._getframe()从调用框架中检索类:

    import sys
    
    def methoddecorator(func):
         callingframe = sys._getframe(1)
         classname = callingframe.f_code.co_name
    

    请注意,您可以检索的只是该类的名称;班级本身目前仍在建设中。您可以将项目添加到callingframe.f_locals(映射),它们将成为新类对象的一部分。

  3. 调用方法时访问selfself毕竟是对实例的引用,self.__class__至少是函数定义的原始类的子类。

答案 1 :(得分:1)

我的严格答案是:这是不可能的,因为在执行装饰器时类仍然不存在。

答案越长,取决于您的确切要求。正如我所写,如果它尚不存在,则无法访问该类。一种解决方案是,将装饰的方法标记为稍后“转换”。然后使用元类或类装饰器在创建类之后应用修改。

另一种选择涉及一些魔法。在implements中查找zope.interfaces方法的实现。它可以访问刚刚解析过的有关类的信息。不知道它对你的用例是否足够。

答案 2 :(得分:0)

您可能需要查看descriptors。它们允许您实现访问属性时使用的__get__,并且可以根据对象及其类型返回不同的内容。

答案 3 :(得分:0)

使用方法装饰器为有趣的方法添加一些标记属性,并使用迭代方法的元类,查找标记属性,并执行逻辑。元类代码在创建类时运行,因此它引用了新创建的类。

class MyMeta(object):
  def __new__(...):
    ...
    cls = ...
    ... iterate over dir(cls), find methods having .is_decorated, act on them
    return cls
def decorator(f):
  f.is_decorated = True
  return f
class MyBase(object):
  __metaclass__ = MyMeta
class MyClass(MyBase):
  @decorator
  def bar(self, foo):
    print foo

如果您担心MyClass的程序员忘记使用MyBase,您可以通过对调用程序堆栈帧的全局变量进行操作来强制设置decorator中的元类(sys._getframe() {1}})。