Du liest:
Python Decorator: So erweiterst du bestehende Funktionen

Python Decorator: So erweiterst du bestehende Funktionen

von Pero
14.12.2020

Einführung

In Python werden Funktionen meistens als Baustein für das Implementieren eines Programms angesehen. In einfachen Worten ist eine Funktion ein Bereich im Quellcode, der eine bestimmte Aufgabe übernimmt. Der größte Vorteil ist die Wiederverwendbarkeit der entsprechenden Funktion. In Python existieren einige Möglichkeiten, diese Aufgabe deutlich zu optimieren. Dies beinhaltet neben der Lambda-Funktion auch der sogenannten Python Decorator.

Der Python Decorator übernimmt die Aufgabe, eine Funktion in Python zu verändern. Es ist ein fortgeschrittenes, aber auch sehr machtvolles Instrument. In diesem Artikel werden wir deshalb die Decorators sowie deren Nutzung näher betrachten und kennenlernen.

Funktionen in Python

Bevor wir mit den Decorators starten, müssen wir ein paar grundlegende Punkte der Programmiersprache Python und ihren Funktionen betrachten.

In Python ist grundsätzlich alles ein Objekt. Gleiches gilt auch für die Funktionen. Wir können deshalb auf Funktionen referenzieren, sie an eine Variable binden, an eine andere Funktion übergeben und sogar als Rückgabewert einer Funktion erhalten. Und zusätzlich können wir eine Funktion innerhalb einer anderen Funktion erstellen. Diese ist dann auch nur innerhalb dieser nutzbar.

Betrachtet wir dafür folgendes Beispiel:

def outer():
	print("Ich bin eine aeußere Funktion")
	
outer()
demo = outer
demo()

Zunächst wird die outer()-Funktion ganz normal aufgerufen. Im Anschluss wird die Funktion an die Variable „demo“ gebunden. Wie bereits im obigen Text aufgezählt, ist alles in Python ein Objekt, weshalb sich unsere Variable „demo“ nach der Zuweisung genau wie die outer()-Funktion verhält.

Ausgabewert eines Python Decorators

Eine Funktion kann genauso gut als Argument an eine Funktion übergeben werden:

def outer(fun):
	print("Ich bin eine aeußere Funktion")
	fun()
	
def inner():
	print("Ich bin eine innere Funktion")

outer(inner)

Jetzt existieren zwei parallele Funktionen – outer() und inner()- Die outer()-Funktion erfordert ein Argument. In diesem Beispiel sogar noch eine weitere Funktion. Wir übergeben der outer()-Funktion in unserem Beispiel die inner()-Funktion. In der outer()-Funktion wird dann unsere inner()-Funktion aufgerufen. Dies geschieht in der Zeile „func()“.

Ausgabewerte

Wir können ebenfalls eine Funktion als Rückgabewert einer Funktion festlegen.

def outer():
    def inner():
        print("Ich bin die innere Funktion")

    return inner


demo = outer()
demo()

In dem obigen Beispiel wird die inner()-Funktion jetzt innerhalb der outer()-Funktion definiert und mittels „return inner“ auch als Rückgabewert festgelegt. Außerdem wird die outer()-Funktion an die Variable „demo“ gebunden und in der folgenden Zeile aufgerufen.

demo = outer()

Jetzt wird zunächst die outer()-Funktion aufgerufen. Innerhalb dieser wird unsere inner()-Funktion zurückgegeben, also im Anschluss bzw. als Resultat aufgerufen.

Ausgabewerte durch Aufruf der outer()-Funktion

Der Python Decorator

Nachdem wir die Schlüsselpunkte der Funktionen betrachtet haben, widmen wir uns nun den Decorators.

Ein Decorator ist eine Methode, die einer Funktion eine neue Funktionalität zuordnet und diese als Rückgabewert zurückgibt. Der Decorator erwartet dabei ein oder mehrere Funktionen als Argumente und gibt dann die „dekorierte“ Funktion zurück.

Starten wir dafür mit folgendem Beispiel:

def decorator(fun):
    def inner():
        print("Ich bin die innere Funktion")
        fun()

    return inner


def outer():
    print("Ich bin die aeußere Funktion")


outer()
demo = decorator(outer)
demo()

Die decorator()-Funktion ist ein Decorator, der eine weitere Funktion als Argument erwartet und die „dekorierte“ Funktion dann als Rückgabewert zurückgibt. Betrachten wir dafür noch einmal die decorator()-Funktion genauer.

def decorator(fun):
    def inner():
        print("Ich bin die innere Funktion")
        fun()

    return inner

In der Funktion wird eine weitere Funkton – inner() – definiert und im Anschluss auch als Rückgabewert festgelegt. Wenn wir allerdings genau hinschauen, wird in der inner()-Funktion auch die func()-Funktion aufgerufen. Diese ist außerdem das Argument der decorator()-Funktion.

Wenn wir den Quellcode ausführen erhalten wir letztlich folgendes Ergebnis:

Beispiel Python Decorators

Zunächst wird die outer()-Funktion normal aufgerufen. Deshalb wird „Ich bin die aeußere Funktion“ in der Konsole ausgegeben. Dies beschreibt die normale Funktion der outer()-Funktion. Jetzt betrachten wir den nächsten Fall, also die Übergabe der Funktion in die decorator()-Funktion:

demo = decorator(outer)
demo()

Die outer()-Funktion wird als Argument an die decorator()-Funktion übergeben und diese an die „demo“-Variable gebunden. In der decorator()-Funktion wird die inner()-Funktion, die innerhalb dieser definiert ist, zurückgegeben. Jetzt haben wird die Variable „demo“, die die „dekorierte“, also um eine Funktionalität erweiterte, outer()-Funktion darstellt. Zunächst wird „Ich bin die innere Funktion“ und im Anschluss entsprechend „Ich bin die aeußere Funktion“ ausgegeben. Die outer()-Funktion wurde also unter der alten Betrachtung (dem alleinigen Aufruf) um eine weitere Funktionalität bzw. Ausgabe ergänzt.

Betrachten wir dafür diesen Auszug aus dem obigen Code:

def outer():
	print("Ich bin die aeußere Funktion")

demo = decorator(outer)

Diesen Ausdruck können wir jetzt durch den folgenden ersetzen:

@decorator
def outer():
	print("Ich bin die aeußere Funktion")

Durch diesen Ausdruck erhalten wir eine syntaktische Vereinfachung. Wenden wir dies nun auf unseren gesamten Quellcode an.

def decorator(fun):
	def inner():
		print("Ich bin die innere Funktion")
		fun()
		
	return inner

@decorator
def outer():
	print("Ich bin die aeußere Funktion")

outer()

Immer wenn die outer()-Funktion aufgerufen wird, wird diese automatisch mittels der decorator()-Funktion „dekoriert“, also um die entsprechende Funktionalität erweitert.

Ausgabewert

Funktionen mit Rückgabewert

Aktuell ist in unserer „dekorierten“ Funktion outer() nur ein Aufruf der print()-Funktion implementiert. Es kann allerdings vorkommen, dass wir auch in dieser Funktion einen Rückgabewert definieren wollen. Deshalb schauen wir uns jetzt an, wie wir den Decorator bei einer solchen Funktion entsprechend nutzen können.

def decorator(fun):
	def inner():
		print("Ich bin die innere Funktion")
		fun()
		
	return inner

@decorator
def outer():
	return "Ich bin die aeußere Funktion"

outer()

Im obigen Beispiel sind alle Passagen wie in dem von uns vorher verwendeten Code. Allerdings wird in der outer()-Funktion nicht die print()-Funktion aufgerufen, sondern unser Text als Rückgabe festgelegt.

Python Decorator mit Rückgabewert

Wenn wir das Ergebnis betrachten, stellen wir fest, dass unser Code nicht das tut, was wir erwarten. Um ihn wie gewünscht funktionieren zu lassen, müssen wir den String der outer()-Funktion separat speichern und ausgeben.

def decorator(fun):
	def inner():
		print("Ich bin die innere Funktion")
		rueckgabe = fun()
		print(rueckgabe)
		
	return inner

@decorator
def outer():
	return "Ich bin die aeußere Funktion"

outer()

Betrachten wir die inner()-Funktion genauer.

def inner():
	print("Ich bin die innere Funktion")
	rueckgabe = fun()
print(rueckgabe)
return inner

Statt die Funktion func() einfach aufzurufen, speichern wir den Rückgabewert der Funktion jetzt in der „rueckgabe“-Variable. Diese können wir dann in der nächsten Zeile in eine print()-Funktion eingeben.

Der Rückgabewert durch den Python Decorator

Funktionen mit Argumenten

Es ist auch möglich, dass Funktionen, die „dekoriert“ werden sollen, Argumente erfordern. Wie wir bereits wissen wird die Funktion selbst als Argument in den Decorator eingegeben. Wir müssen also eine Möglichkeit finden, um dies zu implementieren. Dabei können wir ähnlich wie im vorherigen Beispiel vorgehen.

def decorator(fun):
    def inner(str):
        print("Ich bin die innere Funktion")
        return fun(str)

    return inner


@decorator
def outer(str):
    print(str)


outer("Ich bin die aeußere Funktion")

Anstatt nämlich direkt die print()-Funktion aufzurufen oder den Text als Rückgabewert zu definieren, können wir den Text auch als Argument an die outer()-Funktion übergeben. Diesen können wir dann in die print()-Funktion übergeben. Wollen wir dabei das Prinzip des Decorator nutzen, müssen wir den Code auf eine bestimmte Weise implementieren, damit unser übergebener Text auch tatsächlich ausgegeben wird.

Betrachten wir dafür die decorator()-Funktion.

def decorator(fun):
    def inner(str):
        print("Ich bin die innere Funktion")
        return fun(str)

    return inner

Die outer()-Funktion wird wie immer als Argument in die decorator()-Funktion übergeben. Der Parameter „str“ aus der outer()-Funktion wird dabei einfach an die inner()-Funktion übergeben. Nach der zusätzlichen Funktionalität kann dann wieder die func()-Funktion, also unser outer(), aufgerufen werden.

Decorator-Funktionen mit Argumenten.

Aneinanderreihung von Decorators

Bis jetzt haben wir lediglich einen Decorator genutzt. In Python ist es allerdings auch möglich, eine Funktion mehrfach zu erweitern. Dies nennt sich dann Aneinanderreihung von Decorators.

Betrachten wir dafür folgendes Beispiel.

def decorator1(fun):
    def inner():
        print("Ich bin die innere Funktion von decorator1")
        fun()

    return inner


def decorator2(fun):
    def inner():
        print("Ich bin die innere Funktion von decorator2")
        fun()

    return inner


def decorator3(fun):
    def inner():
        print("Ich bin die innere Funktion von decorator3")
        fun()

    return inner


@decorator1
@decorator2
@decorator3
def outer():
    print("Ich bin die aeußere Funktion")


outer()

Jetzt verfügen wir über drei verschiedene Decorators – decorator1(), decorator2(), decorator2(). Die outer()-Funktion wird dabei durch alle drei Decorators erweitert.

@decorator1
@decorator2
@decorator3
def outer():
	print("Ich bin die aeußere Funktion")

Die Funktion wird dabei von Oben nach Unten erweitert bzw. aufgerufen. Führen wir den Code aus und schauen uns das Ergebnis an:

Aneinanderreihungen von mehreren Decoratoren.

Der Python Decorator inklusive Bedingungen

Wir haben gesehen, dass es möglich ist, eine Funktion auf mehrere Weisen gleichzeitig zu erweitern. Jetzt wollen wir die Erweiterung der Funktion von einer gestellten Bedingung abhängig machen. Dabei wird der Benutzer zu einer Eingabe aufgefordert, von der wir im Anschluss unsere Erweiterung abhängig machen können. Dies können wir über einfache If-Else-Ausdrücke lösen.

def decorator1(fun):
	def inner():
		print("Ich bin die innere Funktion von decorator1")
		fun()
	return inner

def decorator2(fun):
	def inner():
		print("Ich bin die innere Funktion von decorator2")
		fun()
	return inner

bedingung = int(input("Geben Sie 1 für 'decorator1' oder 2 für 'decorator2' ein\n“))

if bedingung == 1:
	@decorator1
	def outer():
		print("Ich bin die aeußere Funktion")
elif bedingung == 2:
	@decorator2
	def outer():
		print("Ich bin die aeußere Funktion ")

outer()

Wir haben jetzt zwei verschiedene Decorators – decorator1() und decorator2(). Die outer()-Funktion ist jetzt in den If-Else-Ausdruck eingebettet. Wenn die Bedingung „1“ erfüllt ist, wird mittels decorator1() erweitert. Für „2“ findet die Erweiterung hingegen mithilfe des decorator2() statt.

Python Decorator inklusive Bedingungen

Zusammenfassung – Der Python Decorator

Der Python Decorator ist besonders effizient, wenn eine bereits definierte Funktion um eine Funktionalität erweitert werden soll. Wir haben in diesem Artikel deshalb zunächst die wichtigsten Schlüsselpunkte zum Arbeiten mit Funktionen in Python besprochen. Ebenfalls haben wir das Verwenden der Decorators kennengelernt. Dabei ist die Implementierung der zu erweiternden Funktion nicht von Relevanz, da wir über verschiedene Umsetzungen auch Rückgaben und Argumente händeln können. Zuletzt haben wir besprochen, wie wir eine Aneinanderreihung von Decorators implementieren können bzw. diese an Bedingungen geknüpft werden kann.



Bislang gibt es keine Kommentare. Markier dein Revier und sei der Erste!

Schreibe einen Kommentar

Das könnte dich auch interessieren