Benutzer-Werkzeuge

Webseiten-Werkzeuge


python:decorator
  • kapselt eine vorhandene Funktion in einer anderen Funktion
    • dabei übernimmt die kapselnde Funktion den Namen der gekapselten Funktion

Parametrisierter Dekorator

  • beim Aufrufen von Dekoratoren wird in der folgenden Reihenfolge aufgerufen
    • Funktion1 (ich nenne sie Factory) bekommt die Dekorator-Parameter übergeben
    • Funktion2 (ich nenne sie Dekorator-Funktion) bekommt die aufzurufende Funktion übergeben
    • Funktion3 (ich nenne sie Wraper) bekommt die Parameter für die gekapselte Funktion übergeben

def decofactory(decorator_param):
    def decoratorfunction(func):
        def wrapper(*args, **kwargs):
            func(*args, **kwargs)
        return wrapper
    return decoratorfunction
            

@decoratorfactory(someparameter)
def actual_function(param1, param2):
    pass

  • Technisch ist der Aufruf decofactory(decorator_param)(func) → das gibt dann Wrapper zurück was dann wiederum aufgerufen wird
  • decofactory nimmt die Parameter des Dekorators auf
  • decoratorfunction bekommt die Methode übergeben

Default values in Parametern von Dekoratoren

def decofactory(decorator_param=1):
    def decoratorfunction(func):
        def wrapper(*args, **kwargs):
            func(*args, **kwargs)
        return wrapper
    return decoratorfunction
            

@decoratorfactory
def actual_function(param1, param2):
    pass

  • würde man actual_function aufrufen würde man einen Verweis auf eine Funktion zurückerhalten und die eigentliche Funktion nicht ausgeführt werden
  • der Dekorator wird ohne Parameter aufgerufen (und ohne leere Klammern → aka. kein Funktionsaufruf ohne Parameter)
    • es wird nicht decoratorfactory(Dekorator-Parameter)(func) aufgerufen
    • sondern decoratorfactory(func)
      • dadurch wird decoratorfunction die Funktion übergeben und nicht an decoratorfunction
        • dadurch wiederum kommt der Default-Value in decoratorfunction nicht zum tragen (da ja ein Wert übergeben wurde

Lösung 1

Statt:

@decoratorfactory
def actual_function(param1, param2):
    pass

@decoratorfactory()
def actual_function(param1, param2):
    pass

  • Hinter dem Dekorator steht (), es ist jetzt also ein leerer Funktionsaufruf und letztendlich wird decoratorfactory()(func) aufgerufen.
  • Setzt allerdings voraus die Nutzer des Dekorators das wissen und konsequent beachten
    • die IDE oder Python wird das weglassen von () nicht als Fehler melden

Lösung 2

def decofactory(decorator_param=1):
    def decoratorfunction(func):
        def wrapper(*args, **kwargs):
            func(*args, **kwargs)
        return wrapper
    if callable(decorator_param):
        # Set the default-value 
        decorator_param = 1
        return decoratorfunction(decorator_param)
    return decoratorfunction

  • neu ist „if callable“
    • prüft ob decorator_param eine Funktion übergeben bekommen hat
      • das wäre der Fall wenn der Dekorator ohne „()“ aufgerufen worden wäre, dann würde gleich die zu dekorierende Funktion übergeben werden
      • in diesem Fall wird decoratorfunction mit dem Inhalt von decorator_param (der zu dekorierenden Funktion) aufgerufen → das heißt es wird wrapper zurückgegeben, das ist das was erwartet wird (das der unmittelbare Wrapper zurückkommt)
  • Achtung: decorator_param (der erste Parameter der ersten Funktion) darf natürlich kein callable erwarten - sprich keine Funktion oder Klasse, sonst funktioniert dieser Weg nicht
python/decorator.txt · Zuletzt geändert: 2022/07/15 10:47 von root