Properties statt Getters und Setters¶
Properties¶
Getters (auch als 'Accessors' bekannt) und Setter (auch bekannt als 'Mutators') werden in vielen objektorientierten Programmiersprachen verwendet, um das Prinzip der Datenkapselung sicherzustellen. Die Datenkapselung erfolgt- wie wir in unserer Einführung auf Objektorientierte Programmierung unseres Tutorials erfahren haben - über eine Bündelung der Daten mit Methoden, die auf diesen arbeiten. Diese Methoden sind der Getter zum Abrufen der Daten und der Setter zum Ändern der Daten. Nach diesem Prinzip werden die Attribute einer Klasse privatisiert, um den Code zu verbergen und zu schützen.
Leider ist die Überzeugung weit verbreitet, dass eine richtige Python-Klasse private Attribute mithilfe von Gettern und Setzern kapseln sollte. Sobald diese Programmierenden ein neues Attribut einführen, machen sie es zu einer privaten Variablen und erstellt "automatisch" einen Getter und einen Setter für dieses Attribut. Solche Programmierenden können sogar einen Editor oder eine IDE verwenden, die automatisch Getter und Setter für alle privaten Attribute erstellt. Diese Tools warnen die Programmierenden sogar, wenn sie ein öffentliches Attribut verwenden wollen! Manche haben OOP in anderen Sprachen so gelernt, dass sie die Stirne runzeln oder entsetzt sein werden, wenn sie Folgendes lesen: Die pythonische Art, Attribute einzuführen, besteht darin, sie öffentlich zu machen.
Wir werden dies später erklären. Zunächst zeigen wir im folgenden Beispiel, wie wir eine Klasse auf Java-ähnliche Weise mit Get- und Setzern entwerfen können, um das private Attribut self.__ x:
class P:
def __init__(self, x):
self.__x = x
def get_x(self):
return self.__x
def set_x(self, x):
self.__x = x
Wir zeigen nun, wie man mit dieser Klasse arbeiten kann:
p1 = P(42)
p2 = P(4711)
print(p1.get_x())
p1.set_x(47)
p1.set_x(p1.get_x()+p2.get_x())
print(p1.get_x())
Was halten Sie von dem Ausdruck "p1.set_x(p1.get_x()+p2.get_x())"? Das ist doch hässlich? Es wäre doch viel einfacher, einen Ausdruck wie den folgenden zu schreiben, wenn wir ein öffentliches Attribut x hätten:
p1.x = p1.x + p2.x
Eine solche Zuordnung ist leichter zu schreiben und vor allem leichter zu lesen als der Javaesque-Ausdruck.
Schreiben wir die Klasse P pythonisch um. Kein Getter, kein Setter und anstelle des privaten Attributs self .__ x verwenden wir ein öffentliches:
class P:
def __init__(self,x):
self.x = x
Schön, nicht wahr? Nur drei Codezeilen, wenn wir die leere Zeile nicht zählen!
p1 = P(42)
p2 = P(4711)
print(p1.x)
p1.x = 47
p1.x = p1.x + p2.x
print(p1.x)
"Aber, aber, aber, aber, aber ...", wir können sie heulen und schreien hören, "aber wo bleibt die Datenverkapselung?" Ja, in diesem Fall erfolgt keine Datenkapselung. Wir brauchen es in diesem Fall nicht. Das einzige, was get_x und set_x in unserem Startbeispiel taten, war, "die Daten durchzureichen", ohne etwas Zusätzliches zu tun.
Aber was passiert, wenn wir die Implementierung in Zukunft ändern wollen? Dies ist ein ernstes Argument. Nehmen wir an, wir möchten die Implementierung folgendermaßen ändern: Das Attribut x kann Werte zwischen 0 und 1000 haben. Wenn ein Wert größer als 1000 zugewiesen wird, sollte x auf 1000 gesetzt werden. Entsprechend sollte x auf 0 gesetzt werden, wenn der Wert kleiner als 0 ist.
Es ist einfach, die Methoden unserer ersten P-Klasse anzupassen, um dieses Problem abzudecken. Wir ändern die set_x-Methode entsprechend:
class P:
def __init__(self,x):
self.set_x(x)
def get_x(self):
return self.__x
def set_x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x 