Beginnen wir mit Python-Dekoratoren.
Ein Python-Dekorator ist eine Funktion, die dazu beiträgt, zusätzliche Funktionalitäten zu einer bereits definierten Funktion hinzuzufügen.
In Python ist alles ein Objekt. Funktionen in Python sind Objekte der ersten Klasse, was bedeutet, dass sie von einer Variablen verwiesen, in Listen eingefügt, als Argumente an eine andere Funktion übergeben usw. werden können.
Betrachten Sie den folgenden Code-Ausschnitt.
def dekorator_func(fun):
def wrapper_func():
print("Wrapper-Funktion gestartet")
fun()
print("Angegebene Funktion dekoriert")
# Die Wrapper-Funktion fügt der übergebenen Funktion etwas hinzu und der Dekorator
# gibt die Wrapper-Funktion zurück
return wrapper_func
def sag_tschuess():
print("Tschüss!!")
sag_tschuess = dekorator_func(sag_tschuess)
sag_tschuess()
# Ausgabe:
# Wrapper-Funktion gestartet
# Tschüss!!
# Angegebene Funktion dekoriert
Hier können wir sagen, dass die Dekoratorfunktion unsere sag_tschuess-Funktion geändert und einige zusätzliche Codezeilen hinzugefügt hat.
Python-Syntax für Dekorator
def dekorator_func(fun):
def wrapper_func():
print("Wrapper-Funktion gestartet")
fun()
print("Angegebene Funktion dekoriert")
# Die Wrapper-Funktion fügt der übergebenen Funktion etwas hinzu und der Dekorator
# gibt die Wrapper-Funktion zurück
return wrapper_func
@dekorator_func
def sag_tschuess():
print("Tschüss!!")
sag_tschuess()
Lassen Sie uns alles anhand eines Fallbeispiels durchgehen. Aber bevor wir das tun, sprechen wir über einige OOP-Prinzipien.
Getter und Setter werden in vielen objektorientierten Programmiersprachen verwendet, um das Prinzip der Datenkapselung (das als das Bündeln von Daten mit den Methoden, die auf diese Daten arbeiten, betrachtet wird) sicherzustellen.
Diese Methoden sind natürlich der Getter zum Abrufen der Daten und der Setter zum Ändern der Daten.
Nach diesem Prinzip werden die Attribute einer Klasse privat gemacht, um sie vor anderem Code zu verbergen und zu schützen.
Ja, @property ist im Grunde genommen eine pythonische Art, Getter und Setter zu verwenden.
Python hat ein großartiges Konzept namens Property, das das Leben eines objektorientierten Programmierers viel einfacher macht.
Angenommen, Sie entscheiden sich, eine Klasse zu erstellen, die die Temperatur in Grad Celsius speichern könnte.
class Celsius:
def __init__(self, temperatur = 0):
self.set_temperature(temperatur)
def to_fahrenheit(self):
return (self.get_temperature() * 1.8) + 32
def get_temperature(self):
return self._temperatur
def set_temperature(self, wert):
if wert < -273:
raise ValueError("Temperatur unter -273 ist nicht möglich")
self._temperatur = wert
Refaktorierter Code, So könnten wir es mit 'Property' erreicht haben.
In Python ist property() eine integrierte Funktion, die ein Property-Objekt erstellt und zurückgibt.
Ein Property-Objekt hat drei Methoden, getter(), setter() und delete().
class Celsius:
def __init__(self, temperatur = 0):
self.temperatur = temperatur
def to_fahrenheit(self):
return (self.temperatur * 1.8) + 32
def get_temperature(self):
print("Wert abrufen")
return self.temperatur
def set_temperature(self, wert):
if wert < -273:
raise ValueError("Temperatur unter -273 ist nicht möglich")
print("Wert setzen")
self.temperatur = wert
temperatur = property(get_temperature,set_temperature)
Hier,
temperatur = property(get_temperature,set_temperature)
könnte aufgeteilt werden als,
# leeres Property erstellen
temperatur = property()
# fget zuweisen
temperatur = temperatur.getter(get_temperature)
# fset zuweisen
temperatur = temperatur.setter(set_temperature)
Zu beachten:
- get_temperature bleibt ein Property anstelle einer Methode.
Jetzt können Sie auf den Wert von Temperatur zugreifen, indem Sie Folgendes schreiben.
C = Celsius()
C.temperatur
# anstelle von C.get_temperature()
Wir können weitermachen und die Namen get_temperature und set_temperature nicht definieren, da sie unnötig sind und den Klassen-Namespace verschmutzen.
Der pythonische Weg, mit dem obigen Problem umzugehen, besteht darin, @property zu verwenden.
class Celsius:
def __init__(self, temperatur = 0):
self.temperatur = temperatur
def to_fahrenheit(self):
return (self.temperatur * 1.8) + 32
@property
def temperatur(self):
print("Wert abrufen")
return self.temperatur
@temperatur.setter
def temperatur(self, wert):
if wert < -273:
raise ValueError("Temperatur unter -273 ist nicht möglich")
print("Wert setzen")
self.temperatur = wert
Zu beachtende Punkte -
- Eine Methode, die zum Abrufen eines Werts verwendet wird, ist mit "@property" dekoriert.
- Die Methode, die als Setter fungieren soll, ist mit "@temperature.setter" dekoriert. Wenn die Funktion "x" genannt worden wäre, hätten wir sie mit "@x.setter" dekorieren müssen.
- Wir haben "zwei" Methoden mit dem gleichen Namen und einer unterschiedlichen Anzahl von Parametern geschrieben, "def temperatur(self)" und "def temperatur(self,x)".
Wie Sie sehen können, ist der Code definitiv weniger elegant.
Jetzt sprechen wir über ein praktisches Szenario im wirklichen Leben.
Angenommen, Sie haben eine Klasse wie folgt entworfen:
class UnsereKlasse:
def __init__(self, a):
self.x = a
y = UnsereKlasse(10)
print(y.x)
Nehmen wir weiter an, dass unsere Klasse bei den Kunden beliebt wurde und sie begannen, sie in ihren Programmen zu verwenden. Sie haben alle möglichen Zuweisungen an das Objekt vorgenommen.
Und eines schicksalhaften Tages kam ein vertrauenswürdiger Kunde zu uns und schlug vor, dass "x" ein Wert zwischen 0 und 1000 sein muss; das ist wirklich ein schreckliches Szenario!
Aufgrund von Properties ist es einfach: Wir erstellen eine Property-Version von "x".
class UnsereKlasse:
def __init__(self,x):
self.x = x
@property
def x(self):
return self.__x
@x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
Das ist großartig, oder: Sie können mit der einfachsten denkbaren Implementierung beginnen, und Sie sind frei, später zu einer Property-Version zu migrieren, ohne die Schnittstelle ändern zu müssen! Properties sind also nicht nur ein Ersatz für Getter und Setter!
Sie können diese Implementierung hier überprüfen