Kurs
Verkapselung in Python: Ein umfassender Leitfaden
Verkapselung ist ein grundlegendes objektorientiertes Prinzip in Python. Sie schützt deine Klassen vor versehentlichen Änderungen oder Löschungen und fördert die Wiederverwendbarkeit und Wartbarkeit des Codes. Betrachte diese einfache Klassendefinition:
class Smartphone:
def __init__(self, brand, os):
self.brand = brand
self.os = os
iphone = Smartphone("Apple", "iOS 17")
Viele Python-Programmierer definieren Klassen wie diese. Es ist jedoch weit von den Best Practices entfernt, die professionelle Pythonistas befolgen. Das Problem mit dieser Klasse wird deutlich, wenn du versuchst, ihre Daten zu ändern:
iphone.os = "Android"
print(iphone.os)
Android
Stell dir vor, ein iPhone läuft auf Android - was für ein Skandal! Es ist klar, dass wir unserer Klasse Grenzen setzen müssen, damit die Benutzer ihre Attribute nicht nach Belieben ändern können.
Wenn sie sich dazu entschließen, sie zu ändern, müssen die Änderungen unter unseren Bedingungen und nach unseren Regeln erfolgen. Aber wir wollen trotzdem, dass die .os
oder .brand
Attribute an der Oberfläche gleich bleiben.
All diese Dinge können mit Hilfe von Kapselung gefördert werden und in diesem Lernprogramm werden wir alles darüber lernen. Lass uns loslegen!
Wie wird die Kapselung in Python erreicht?
Python unterstützt die Kapselung durch Konventionen und Programmierpraktiken und nicht durch erzwungene Zugriffsmodifikatoren. Das Grundprinzip, das hinter einem Großteil des Python-Codes steht, lautet: "Wir sind hier alle erwachsen". Wir können die Kapselung nur als bloße Konvention implementieren und von anderen Python-Entwicklern erwarten, dass sie unserem Code vertrauen und ihn respektieren.
In anderen OOP-Sprachen wie Java und C++ wird die Kapselung durch Zugriffsmodifikatoren wie public
, private
oder protected
strikt durchgesetzt, aber in Python gibt es diese nicht:
Feature | Python | Java | C++ |
---|---|---|---|
Zugriffsmodifikatoren | Keine erzwungenen Modifikatoren; verwendet Namenskonventionen (_protected , __private ) |
Durchgesetzt mit public , protected , private keywords |
Durchgesetzt mit public , protected , private keywords |
Getter/Setter-Methoden | Optional, oft verwendet mit @property Dekorateur für kontrollierten Zugang |
Übliche Praxis, typischerweise als Methoden implementiert | Übliche Praxis, typischerweise als Methoden implementiert |
Datenzugang | Zugänglich über Namenskonventionen; hängt von der Disziplin der Entwickler ab | Kontrolliert durch Zugriffsmodifikatoren; erzwungen durch den Compiler | Kontrolliert durch Zugriffsmodifikatoren; erzwungen durch den Compiler |
Philosophie | "Wir sind hier alle erwachsen" - beruht auf Konventionen und Vertrauen | Strikte Durchsetzung der Zugangskontrolle | Strikte Durchsetzung der Zugangskontrolle |
Die meisten, wenn nicht sogar alle, Kapselungstechniken, die ich dir zeigen werde, sind also Python-Konventionen. Sie können leicht gebrochen werden, wenn du dich dafür entscheidest. Aber ich vertraue darauf, dass du sie bei deinen eigenen Entwicklungsprojekten respektierst und befolgst.
Zugriffsmodifikatoren in Python
Nehmen wir an, wir haben diese einfache Klasse:
class Tree:
def __init__(self, height):
self.height = height
pine = Tree(20)
print(pine.height)
20
Es hat ein einziges Höhenattribut, das wir drucken können. Das Problem ist, dass wir es auch ändern können, wie wir wollen:
pine.height = 50
pine.height
50
pine.height = "Grandma"
pine.height
'Grandma'
Wie sagen wir den Nutzerinnen und Nutzern also, dass sie die Höhe nicht verändern dürfen? Nun, wir könnten es in ein geschütztes Mitglied verwandeln, indem wir einen einzelnen vorangestellten Unterstrich hinzufügen:
class Tree:
def __init__(self, height):
self._height = height
pine = Tree(20)
pine._height
20
Wer sich dieser Konvention bewusst ist, weiß, dass er nur auf das Attribut zugreifen kann und dass wir dringend davon abraten, es zu verwenden und zu verändern. Aber wenn sie wollen, können sie es ändern, oh ja.
Wie können wir auch das verhindern? Indem du eine andere Konvention verwendest - mache das Attribut zu einem privaten Mitglied , indem du doppelte Unterstriche hinzufügst:
class Tree:
def __init__(self, height):
self.__height = height
pine = Tree(20)
pine.__height
AttributeError: 'Tree' object has no attribute '__height'
Jetzt wird Python eine Fehlermeldung ausgeben, wenn jemand versucht, auf das Attribut zuzugreifen, geschweige denn es zu ändern.
Aber hast du bemerkt, was wir gerade getan haben? Wir haben die einzigen Informationen, die sich auf unsere Objekte beziehen, vor den Nutzern versteckt. Unsere Klasse wurde gerade nutzlos, weil sie keine öffentlichen Attribute hat.
Wie können wir also die Höhe des Baums für die Nutzer/innen sichtbar machen, aber trotzdem kontrollieren, wie sie darauf zugreifen und sie verändern? Wir wollen zum Beispiel, dass die Baumhöhen innerhalb eines bestimmten Bereichs liegen und nur ganzzahlige Werte haben. Wie können wir das durchsetzen?
An dieser Stelle könnte sich dein Java-benutzender Freund zu Wort melden und vorschlagen, Getter- und Setter-Methoden zu verwenden. Lass uns das also zuerst ausprobieren:
class Tree:
def __init__(self, height):
self.__height = height
def get_height(self):
return self.__height
def set_height(self, new_height):
if not isinstance(new_height, int):
raise TypeError("Tree height must be an integer")
if 0 < new_height <= 40:
self.__height = new_height
else:
raise ValueError("Invalid height for a pine tree")
pine = Tree(20)
pine.get_height()
20
Auf diese Weise erstellst du ein privates Attribut __height
, lässt aber die Benutzer mit den Methoden get_height
und set_height
kontrolliert darauf zugreifen und es ändern.
pine.set_height(25)
pine.get_height()
25
Bevor du einen neuen Wert festlegst, stellt set_height
sicher, dass die neue Höhe innerhalb eines bestimmten Bereichs und numerisch ist.
pine.set_height("Password")
TypeError: Tree height must be an integer
Aber diese Methoden scheinen zu viel für eine einfache Operation zu sein. Außerdem ist es hässlich, so einen Code zu schreiben:
# Increase height by 5
pine.set_height(pine.get_height() + 5)
Wäre es nicht schöner und lesbarer, wenn wir diesen Code schreiben könnten:
pine.height += 5
und trotzdem den richtigen Datentyp und Bereich für die Höhe durchsetzen? Die Antwort ist ja und wir werden im nächsten Abschnitt lernen, wie man genau das macht.
@property-Dekorator in Python-Klassen verwenden
Wir führen eine neue Technik ein - die Erstellung von Eigenschaften für Attribute:
class Tree:
def __init__(self, height):
# First, create a private or protected attribute
self.__height = height
@property
def height(self):
return self.__height
pine = Tree(17)
pine.height
17
Wir möchten, dass die Nutzer auf ein verstecktes Attribut namens __height
zugreifen können, als wäre es ein normales Attribut namens height
. Um dies zu erreichen, definieren wir eine Methode namens height
, die self.__height
zurückgibt, und schmücken sie mit @property
.
Jetzt können wir height
aufrufen und auf das private Attribut zugreifen:
pine.height
17
Aber das Beste daran ist, dass die Nutzer sie nicht verändern können:
pine.height = 15
AttributeError: can't set attribute 'height'
Wir fügen also eine weitere Methode namens height(self, new_height)
hinzu, die von einem height.setter
Dekorator umhüllt wird. In dieser Methode implementieren wir die Logik, die den gewünschten Datentyp und den Bereich für die Höhe erzwingt:
class Tree:
def __init__(self, height):
self.__height = height
@property
def height(self):
return self.__height
@height.setter
def height(self, new_height):
if not isinstance(new_height, int):
raise TypeError("Tree height must be an integer")
if 0 < new_height <= 40:
self.__height = new_height
else:
raise ValueError("Invalid height for a pine tree")
Wenn ein/e Nutzer/in nun versucht, das Attribut height
zu ändern, wird @height.setter
aufgerufen, um sicherzustellen, dass der richtige Wert übergeben wird:
pine = Tree(10)
pine.height = 33 # Calling @height.setter
pine.height = 45 # An error is raised
ValueError: Invalid height for a pine tree
Wir können auch anpassen, wie auf das Attribut height
durch die Punkt-Notation mit @height.getter
zugegriffen wird:
class Tree:
def __init__(self, height):
self.__height = height
@property
def height(self):
return self.__height
@height.getter
def height(self):
# You can return a custom version of height
return f"This tree is {self.__height} meters"
pine = Tree(33)
pine.height
'This tree is 33 meters'
Auch wenn wir pine
mit einer ganzzahligen Höhe erstellt haben, können wir den Wert mit @height.getter
ändern.
Dies waren Beispiele dafür, wie wir die Kapselung in einer Python-Klasse fördern können. Vergiss nicht, dass die Verkapselung immer noch eine Konvention ist, weil wir das interne __height
private member immer noch brechen können:
pine._Tree__height = "Gotcha!"
pine.height
'This tree is Gotcha! meters'
Alles in Python-Klassen ist öffentlich, auch die privaten Methoden. Das ist kein Designfehler, sondern ein Beispiel für den "Wir sind doch alle erwachsen"-Ansatz.
Hier ist ein Überblick über die Python-Zugriffsmodifikatoren, die wir gerade besprochen haben:
Modifikator/Konvention | Beschreibung | Beispiel |
---|---|---|
Öffentlich | Standardzugriffsstufe; Attribute und Methoden sind von außerhalb der Klasse zugänglich | self.attribute |
Geschützt | Durch einen einzelnen Unterstrich gekennzeichnet; eine Konvention zur Kennzeichnung eines teilweise eingeschränkten Zugangs | self._attribute |
Privat | Durch doppelte Unterstriche gekennzeichnet; das Mangeln von Namen bietet eine begrenzte Zugangsbeschränkung | self.__attribute |
Eigenschaften | Ermöglicht den kontrollierten Zugriff auf private Attribute mit der @property decorator |
@property def attribute(self): |
Nur-Lese-Eigenschaften | Nein @attribute.setter ; erlaubt den Nur-Lese-Zugriff auf ein Attribut |
@property def attribute(self): |
Best Practices bei der Implementierung der Kapselung
Es gibt eine Reihe von Best Practices, die du befolgen kannst, um sicherzustellen, dass dein Code mit dem von anderen erfahrenen OOPistas geschriebenen Code übereinstimmt:
- Erstelle geschützte oder private Attribute oder Methoden, wenn sie nur von dir verwendet werden. Geschützte oder private Mitglieder werden aus der Dokumentation ausgeschlossen und signalisieren anderen, dass sie von dir, dem Entwickler, ohne Vorankündigung geändert werden können, was sie davon abhält, sie zu verwenden.
- Du musst nicht immer Eigenschaften für jedes einzelne Klassenattribut erstellen. Bei großen Klassen mit vielen Attributen kann das Schreiben von
attr.getter
undattr.setter
Methoden zu einem Kopfzerbrechen werden. - Erwäge, jedes Mal eine Warnung auszulösen, wenn ein Benutzer auf ein geschütztes Mitglied zugreift (
_
). - Verwende private Mitglieder nur sparsam, da sie den Code für diejenigen unlesbar machen können, die mit dieser Konvention nicht vertraut sind.
- Gib der Klarheit den Vorzug vor der Unklarheit. Da die Kapselung die Wartbarkeit des Codes und den Datenschutz verbessern soll, solltest du wichtige Implementierungsdetails deiner Klasse nicht vollständig verstecken.
- Wenn du schreibgeschützte Eigenschaften erstellen willst, implementiere nicht die Methode
@attr.setter
. Die Nutzer können auf die Immobilie zugreifen, sie aber nicht verändern. - Denke immer daran, dass die Kapselung eine Konvention ist und kein erzwungener Aspekt der Python-Syntax.
- Für einfache Klassen solltest du die Verwendung von Datenklassen in Betracht ziehen, mit denen du die Klassenkapselung mit einer einzigen Zeile Code ermöglichen kannst. Datenklassen hingegen sind für einfachere Klassen mit vorhersehbaren Attributen und Methoden gedacht. In diesem Dataclasses-Tutorial erfährst du mehr.
Fazit und weitere Ressourcen
In diesem Lernprogramm haben wir eine der wichtigsten Säulen der objektorientierten Programmierung in Python kennengelernt: die Kapselung.
Mit der Kapselung kannst du einen kontrollierten Zugriff auf Daten definieren, die in Objekten deiner Klasse gespeichert sind. So kannst du sauberen, lesbaren und effizienten Code schreiben und versehentliche Änderungen oder Löschungen deiner Klassendaten verhindern.
Hier findest du weitere Ressourcen, um dein OOP-Wissen zu erweitern:
Python von Grund auf lernen
FAQs
Wie unterscheidet sich die Kapselung von der Abstraktion in Python?
Bei der Kapselung geht es darum, Daten und Methoden, die mit den Daten arbeiten, in einer Einheit, z. B. einer Klasse, zu bündeln und den Zugriff auf einige Komponenten zu beschränken. Bei der Abstraktion hingegen geht es darum, die komplexe Realität zu verbergen und nur die wesentlichen Teile sichtbar zu machen. Während sich die Kapselung auf die Einschränkung des Zugriffs konzentriert, geht es bei der Abstraktion um die Vereinfachung der Interaktionen.
Kann Kapselung auch in der funktionalen Programmierung verwendet werden oder ist sie nur in der OOP möglich?
Kapselung ist ein Konzept, das eng mit der objektorientierten Programmierung (OOP) verbunden ist und in der funktionalen Programmierung normalerweise nicht verwendet wird. Die funktionale Programmierung legt den Schwerpunkt auf Unveränderlichkeit und zustandslose Funktionen, im Gegensatz zur OOP, die sich auf Objekte und die Kapselung von Daten in diesen Objekten konzentriert.
Was sind die häufigsten Fallstricke, die bei der Implementierung von Kapselung in Python zu vermeiden sind?
Häufige Fallstricke sind die übermäßige Verwendung privater Variablen, was die Wartung des Codes erschweren kann, und das Fehlen geeigneter Zugriffsmethoden (Getter/Setter), was zu anfälligem Code führen kann. Außerdem vergessen die Entwickler vielleicht, dass die Kapselung in Python eine Konvention ist und daher umgangen werden kann.
Wie kann die Verkapselung die Codesicherheit verbessern?
Die Kapselung kann die Codesicherheit verbessern, indem sie den Zugriff auf den internen Zustand eines Objekts einschränkt und unautorisierte Änderungen verhindert. Indem sie kontrollieren, wie auf die Daten zugegriffen wird und wie sie verändert werden, können die Entwickler sicherstellen, dass die Daten gültig und konsistent bleiben.
Wie wirkt sich die Verwendung des @property-Dekorators auf die Leistung in Python aus?
Die Verwendung des @property
Dekorators kann aufgrund der zusätzlichen Methodenaufrufe einen leichten Overhead verursachen, aber in den meisten Fällen sind die Auswirkungen vernachlässigbar. Die Vorteile einer besseren Lesbarkeit und Wartbarkeit des Codes überwiegen oft die geringfügigen Leistungseinbußen.
Ist es möglich, in Python eine strikte Kapselung wie in Java oder C++ durchzusetzen?
Python setzt die Kapselung nicht so streng durch wie Java oder C++. Seine Philosophie basiert auf Konventionen und dem Verständnis, dass die Entwickler die beabsichtigte Nutzung privater und geschützter Mitglieder respektieren werden. Die Verwendung von Namenskonventionen wie Unterstrichen kann jedoch einen Missbrauch verhindern.
Was sind einige praktische Anwendungen der Kapselung in Python-Projekten?
Die Verkapselung wird verwendet, um sensible Daten in Anwendungen wie Bankensoftware zu schützen, in denen Kontodaten versteckt werden. Sie wird auch in Bibliotheken und Frameworks verwendet, um komplexe Implementierungsdetails zu verbergen und Entwicklern die Interaktion mit einfachen Schnittstellen zu ermöglichen.
Wie kann ich gekapselten Code in Python effektiv testen?
Das Testen von gekapseltem Code beinhaltet das Schreiben von Unit-Tests für die öffentliche Schnittstelle einer Klasse. Durch das Testen der Methoden und Eigenschaften, die für den Benutzer sichtbar sind, stellst du sicher, dass die internen Zustandsänderungen korrekt verwaltet werden und dass die Kapselungslogik funktioniert.
Kann die Kapselung auf Python-Module angewendet werden, oder ist sie auf Klassen beschränkt?
Während die Kapselung in erster Linie mit Klassen in Verbindung gebracht wird, kann das Konzept auf Module ausgeweitet werden, indem Funktionen oder Variablen mit einem Unterstrich vorangestellt werden, um die private Nutzung innerhalb eines Moduls zu signalisieren.
Was hat die Kapselung mit der Polymorphie in Python zu tun?
Kapselung und Polymorphismus sind beides Prinzipien der OOP, dienen aber unterschiedlichen Zwecken. Die Kapselung schränkt den Zugriff auf bestimmte Teile eines Objekts ein, während die Polymorphie es ermöglicht, dass Objekte als Instanzen ihrer Elternklasse behandelt werden, was die Erweiterung und Wartung des Codes erleichtert. Sie können zusammen verwendet werden, um flexible und sichere Codestrukturen zu schaffen.
Ich bin ein Data Science Content Creator mit über 2 Jahren Erfahrung und einem der größten Follower auf Medium. Ich schreibe gerne ausführliche Artikel über KI und ML mit einem etwas sarkastischen Stil, denn man muss etwas tun, damit sie nicht so langweilig sind. Ich habe mehr als 130 Artikel verfasst und einen DataCamp-Kurs gemacht, ein weiterer ist in Vorbereitung. Meine Inhalte wurden von über 5 Millionen Augenpaaren gesehen, von denen 20.000 zu Followern auf Medium und LinkedIn wurden.
Setze deine Python-Reise heute fort!
Lernpfad
Python Programming
Kurs
Writing Efficient Python Code
Der Blog
Die 32 besten AWS-Interview-Fragen und Antworten für 2024
Der Blog
Top 30 Generative KI Interview Fragen und Antworten für 2024
Hesam Sheikh Hassani
15 Min.
Der Blog
Die 20 besten Snowflake-Interview-Fragen für alle Niveaus
Nisha Arya Ahmed
20 Min.