一个实例方法的Python装饰器可以访问类吗?

嗨,我有一些大致如下。 基本上我需要从其定义中的实例方法使用的装饰器中访问实例方法的类。

def decorator(view): # do something that requires view's class print view.im_class return view class ModelA(object): @decorator def a_method(self): # do some stuff pass 

代码是这样的

AttributeError: 'function' object has no attribute 'im_class'

我发现类似的问题/答案 – Python装饰器让函数忘记它属于一个类,并在Python装饰器中的类 – 但这些依靠一个解决方法抓取第一个参数在运行时抓取实例。 就我而言,我会根据从class上收集的信息来调用这个方法,所以我不能等待电话进来。

谢谢。

如果你正在使用Python 2.6或更高版本,你可以使用一个类装饰器,也许这样(警告:未经testing的代码)。

 def class_decorator(cls): for name, method in cls.__dict__.iteritems(): if hasattr(method, "use_class"): # do something with the method and class print name, cls return cls def method_decorator(view): # mark the method as something that requires view's class view.use_class = True return view @class_decorator class ModelA(object): @method_decorator def a_method(self): # do some stuff pass 

方法装饰器通过添加“use_class”属性将方法标记为感兴趣的方法 – 函数和方法也是对象,因此您可以将附加元数据附加到它们。

类创build完成之后,类装饰器会遍历所有的方法,并在已标记的方法上执行所需的任何操作。

如果你想要所有的方法受到影响,那么你可以省略方法装饰器,只使用类装饰器。

正如其他人所指出的,这个类在装饰器被调用的时候还没有被创build。 但是 ,可以使用装饰器参数注释函数对象,然后在元类的__new__方法中重新装饰函数。 你需要直接访问函数的__dict__属性,至less对于我来说, func.foo = 1导致了一个AttributeError。

正如ant所指出的,你不能从课堂上得到课堂的提示。 但是,如果您有兴趣区分不同的类(而不是操作实际的类types对象),则可以为每个类传递一个string。 您还可以使用类风格的装饰器将其他parameter passing给装饰器。

 class Decorator(object): def __init__(self,decoratee_enclosing_class): self.decoratee_enclosing_class = decoratee_enclosing_class def __call__(self,original_func): def new_function(*args,**kwargs): print 'decorating function in ',self.decoratee_enclosing_class original_func(*args,**kwargs) return new_function class Bar(object): @Decorator('Bar') def foo(self): print 'in foo' class Baz(object): @Decorator('Baz') def foo(self): print 'in foo' print 'before instantiating Bar()' b = Bar() print 'calling b.foo()' b.foo() 

打印:

 before instantiating Bar() calling b.foo() decorating function in Bar in foo 

另请参阅Bruce Eckel关于装饰者的页面。

问题是,当装饰器被称为类尚不存在。 尝试这个:

 def loud_decorator(func): print("Now decorating %s" % func) def decorated(*args, **kwargs): print("Now calling %s with %s,%s" % (func, args, kwargs)) return func(*args, **kwargs) return decorated class Foo(object): class __metaclass__(type): def __new__(cls, name, bases, dict_): print("Creating class %s%s with attributes %s" % (name, bases, dict_)) return type.__new__(cls, name, bases, dict_) @loud_decorator def hello(self, msg): print("Hello %s" % msg) Foo().hello() 

这个程序将输出:

 Now decorating <function hello at 0xb74d35dc> Creating class Foo(<type 'object'>,) with attributes {'__module__': '__main__', '__metaclass__': <class '__main__.__metaclass__'>, 'hello': <function decorated at 0xb74d356c>} Now calling <function hello at 0xb74d35dc> with (<__main__.Foo object at 0xb74ea1ac>, 'World'),{} Hello World 

正如你所看到的,你将不得不找出一个不同的方式去做你想做的事情。

这是一个简单的例子:

 def mod_bar(cls): # returns modified class def decorate(fcn): # returns decorated function def new_fcn(self): print self.start_str print fcn(self) print self.end_str return new_fcn cls.bar = decorate(cls.bar) return cls @mod_bar class Test(object): def __init__(self): self.start_str = "starting dec" self.end_str = "ending dec" def bar(self): return "bar" 

输出是:

 >>> import Test >>> a = Test() >>> a.bar() starting dec bar ending dec 

最好的方法是创build一个存储在方法中的临时caching,然后使用其他方法(Flask将使用register类方法register类)来实际包装该方法。

您可以重复使用此模式,这次使用元类,以便您可以在导入时包装该方法。

 def route(rule, **options): """A decorator that is used to define custom routes for methods in FlaskView subclasses. The format is exactly the same as Flask's `@app.route` decorator. """ def decorator(f): # Put the rule cache on the method itself instead of globally if not hasattr(f, '_rule_cache') or f._rule_cache is None: f._rule_cache = {f.__name__: [(rule, options)]} elif not f.__name__ in f._rule_cache: f._rule_cache[f.__name__] = [(rule, options)] else: f._rule_cache[f.__name__].append((rule, options)) return f return decorator 

在实际的类(你可以使用元类来完成):

 @classmethod def register(cls, app, route_base=None, subdomain=None, route_prefix=None, trailing_slash=None): for name, value in members: proxy = cls.make_proxy_method(name) route_name = cls.build_route_name(name) try: if hasattr(value, "_rule_cache") and name in value._rule_cache: for idx, cached_rule in enumerate(value._rule_cache[name]): # wrap the method here 

来源: https : //github.com/apiguy/flask-classy/blob/master/flask_classy.py

这是一个古老的问题,但遇到了金星。 http://venusian.readthedocs.org/en/latest/

它似乎有能力装饰方法,并给你访问类和方法,而这样做。 请注意,调用setattr(ob, wrapped.__name__, decorated)不是使用venusian的典型方式,有些失败的目的。

无论哪种方式…下面的例子是完整的,应该运行。

 import sys from functools import wraps import venusian def logged(wrapped): def callback(scanner, name, ob): @wraps(wrapped) def decorated(self, *args, **kwargs): print 'you called method', wrapped.__name__, 'on class', ob.__name__ return wrapped(self, *args, **kwargs) print 'decorating', '%s.%s' % (ob.__name__, wrapped.__name__) setattr(ob, wrapped.__name__, decorated) venusian.attach(wrapped, callback) return wrapped class Foo(object): @logged def bar(self): print 'bar' scanner = venusian.Scanner() scanner.scan(sys.modules[__name__]) if __name__ == '__main__': t = Foo() t.bar() 

您将有权访问修饰器应该返回的装饰方法中调用该方法的对象的类。 像这样:

 def decorator(method): # do something that requires view's class def decorated(self, *args, **kwargs): print 'My class is %s' % self.__class__ method(self, *args, **kwargs) return decorated 

使用你的ModelA类,这是这样做的:

 >>> obj = ModelA() >>> obj.a_method() My class is <class '__main__.ModelA'> 

正如马克所说:

  1. 任何装饰器都被称为BEFORE类,所以装饰器是未知的。
  2. 我们可以标记这些方法,并在稍后进行必要的后处理。
  3. 对于后期处理,我们有两个选项:自动在类定义的末尾或在应用程序运行之前的某个地方。 我更喜欢使用基类的第一个选项,但是您也可以遵循第二种方法。

此代码显示了这可能如何使用自动后处理:

 def expose(**kw): "Note that using **kw you can tag the function with any parameters" def wrap(func): name = func.func_name assert not name.startswith('_'), "Only public methods can be exposed" meta = func.__meta__ = kw meta['exposed'] = True return func return wrap class Exposable(object): "Base class to expose instance methods" _exposable_ = None # Not necessary, just for pylint class __metaclass__(type): def __new__(cls, name, bases, state): methods = state['_exposed_'] = dict() # inherit bases exposed methods for base in bases: methods.update(getattr(base, '_exposed_', {})) for name, member in state.items(): meta = getattr(member, '__meta__', None) if meta is not None: print "Found", name, meta methods[name] = member return type.__new__(cls, name, bases, state) class Foo(Exposable): @expose(any='parameter will go', inside='__meta__ func attribute') def foo(self): pass class Bar(Exposable): @expose(hide=True, help='the great bar function') def bar(self): pass class Buzz(Bar): @expose(hello=False, msg='overriding bar function') def bar(self): pass class Fizz(Foo): @expose(msg='adding a bar function') def bar(self): pass print('-' * 20) print("showing exposed methods") print("Foo: %s" % Foo._exposed_) print("Bar: %s" % Bar._exposed_) print("Buzz: %s" % Buzz._exposed_) print("Fizz: %s" % Fizz._exposed_) print('-' * 20) print('examine bar functions') print("Bar.bar: %s" % Bar.bar.__meta__) print("Buzz.bar: %s" % Buzz.bar.__meta__) print("Fizz.bar: %s" % Fizz.bar.__meta__) 

输出产量:

 Found foo {'inside': '__meta__ func attribute', 'any': 'parameter will go', 'exposed': True} Found bar {'hide': True, 'help': 'the great bar function', 'exposed': True} Found bar {'msg': 'overriding bar function', 'hello': False, 'exposed': True} Found bar {'msg': 'adding a bar function', 'exposed': True} -------------------- showing exposed methods Foo: {'foo': <function foo at 0x7f7da3abb398>} Bar: {'bar': <function bar at 0x7f7da3abb140>} Buzz: {'bar': <function bar at 0x7f7da3abb0c8>} Fizz: {'foo': <function foo at 0x7f7da3abb398>, 'bar': <function bar at 0x7f7da3abb488>} -------------------- examine bar functions Bar.bar: {'hide': True, 'help': 'the great bar function', 'exposed': True} Buzz.bar: {'msg': 'overriding bar function', 'hello': False, 'exposed': True} Fizz.bar: {'msg': 'adding a bar function', 'exposed': True} 

请注意,在这个例子中:

  1. 我们可以用任意的参数来注释任何函数。
  2. 每个class级都有自己的暴露方法。
  3. 我们也可以inheritance公开的方法。
  4. 方法可以重写,因为曝光function被更新。

希望这可以帮助