* 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