Kann ein Python-Dekorator einer Instanz-Methode auf die Klasse zugreifen?

Hallo ich habe etwas grob wie das folgende. Grundsätzlich muss ich auf die Klasse einer Instanzmethode von einem Dekorator zugreifen, der bei der Instanzmethode in seiner Definition verwendet wird.

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 

Der Code as-is gibt

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

Ich habe ähnliche Frage / Antworten gefunden – Python-Dekorator macht Funktion vergessen, dass es zu einer Klasse gehört und Klasse in Python-Dekorateur – aber diese verlassen sich auf eine Workaround, die die Instanz zur Laufzeit packt, indem sie den ersten Parameter schnappt. In meinem Fall werde ich die Methode anrufen, die auf den Informationen basiert, die von seiner Klasse gelesen wurden, also kann ich nicht auf einen Anruf warten, um hereinzukommen.

Vielen Dank.

8 Solutions collect form web for “Kann ein Python-Dekorator einer Instanz-Methode auf die Klasse zugreifen?”

Wenn du Python 2.6 oder später benutzt hast, kannst du einen Klassendekorateur verwenden, vielleicht so etwas wie das (Warnung: Ungetesteter Code).

 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 

Die Methode Dekorator markiert die Methode als eine, die von Interesse ist, indem sie ein "use_class" Attribut – Funktionen und Methoden sind auch Objekte, so können Sie zusätzliche Metadaten zu ihnen hinzufügen.

Nachdem die Klasse erstellt worden ist, geht der Klassendekorateur dann alle Methoden durch und tut, was bei den markierten Methoden benötigt wird.

Wenn Sie wollen, dass alle Methoden betroffen sind, dann könnten Sie die Methode Dekorateur verlassen und verwenden Sie einfach die Klasse Dekorateur.

Wie andere schon erwähnt haben, ist die Klasse zum Zeitpunkt des Aufbaus des Dekorators nicht geschaffen worden. Es ist jedoch möglich, das Funktionsobjekt mit den Dekoratorparametern zu kommentieren und dann die Funktion in der Methode der __new__ Methode der __new__ neu zu verzieren. Du musst direkt auf das Attribut __dict__ der Funktion __dict__ , da zumindest für mich func.foo = 1 einen AttributeError hervorgebracht hat.

Wie Ameisen angedeutet haben, kann man in der Klasse keinen Hinweis auf die Klasse bekommen. Wenn Sie jedoch daran interessiert sind, zwischen verschiedenen Klassen zu unterscheiden (nicht das eigentliche Klassenobjekt zu manipulieren), können Sie für jede Klasse einen String übergeben. Sie können auch passieren, was andere Parameter, die Sie gerne mit dem Dekorateur mit Klassen-Stil Dekorateure.

 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() 

Drucke:

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

Siehe auch Bruce Eckels Seite auf Dekorateure.

Das Problem ist, dass, wenn der Dekorateur genannt wird, die Klasse noch nicht existiert. Versuche dies:

 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() 

Dieses Programm wird ausgegeben:

 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 

Wie du siehst, wirst du einen anderen Weg finden, um zu tun, was du willst.

Hier ist ein einfaches Beispiel:

 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" 

Die Ausgabe ist:

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

Was klappen-nobel macht, erstellt einen temporären Cache, den er auf der Methode speichert, dann benutzt er etwas anderes (die Tatsache, dass Flask die Klassen mit einer register Klassen-Methode register wird), um die Methode tatsächlich zu verpacken.

Sie können dieses Muster wiederverwenden, diesmal mit einer Metaklasse, so dass Sie die Methode bei der Importzeit einpacken können.

 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 

Auf der eigentlichen Klasse (Sie könnten das gleiche mit einer Metaklasse machen):

 @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 

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

Das ist eine alte Frage, aber stieß auf Venus. http://venusian.readthedocs.org/de/latest/

Es scheint, die Fähigkeit zu haben, Methoden zu verzieren und Ihnen Zugriff auf die Klasse und die Methode zu geben, während dies zu tun. Anmerkung tht calling setattr(ob, wrapped.__name__, decorated) ist nicht die typische Art, venusian zu benutzen und etwas besiegt den Zweck.

So oder so … das untenstehende Beispiel ist komplett und sollte laufen.

 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() 

Sie haben Zugang zu der Klasse des Objekts, auf dem die Methode in der verzierten Methode aufgerufen wird, die Ihr Dekorateur zurückgeben sollte. Wie so:

 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 

Mit deiner ModelA-Klasse, hier ist das, was das tut:

 >>> obj = ModelA() >>> obj.a_method() My class is <class '__main__.ModelA'> 
  • Python, verwirrt in dekorieren und schließen
  • Zugriff auf einen Dekorateur in einer übergeordneten Klasse vom Kind in Python
  • Route zum view_func mit den gleichen Dekorateuren "Flasche"
  • Was macht functools.wraps?
  • Verwenden von Python-Eigenschaft () innerhalb einer Methode
  • Wie man eine Python-Dekorator-Funktion in Flasche mit Argumenten (für die Autorisierung)
  • Dekorateure in der python-standard-lib (@deprecated speziell)
  • Python-Dekorateur, der eine Funktion mit einem oder mehreren Argumenten zurückgibt
  • Flasche Hit Dekorateur vor vor dem ersten Signal brennt
  • Funktion Ortsname Bindung aus einem äußeren Bereich
  • Ändern Sie ein Attribut einer Funktion in seinem eigenen Körper?
  • Python ist die beste Programmiersprache der Welt.