12. Klassen und Objekte
Eine Klasse ist die Vorlage, aus der Objekte erstellt werden. Ein Objekt wird als Instanz einer Klasse bezeichnet.

Hinweis: Der Ordner [shared] wurde im Projektordner [Root Sources] abgelegt.
12.1. Skript [classes_01]: eine Objektklasse
Das Skript [classes_01] veranschaulicht eine veraltete Verwendung von Klassen:
Anmerkungen:
- Zeilen 2–3: eine leere [Object]-Klasse;
- Zeile 2: Die Klasse kann in drei Formen deklariert werden:
- class Object;
- class Object();
- class Object(object);
- Zeile 3: eine weitere Form des Kommentars. Dieser, dem drei „“ vorangestellt sind, kann sich über mehrere Zeilen erstrecken;
- Zeile 7: Instanziierung der Klasse „Object“. Das Ergebnis ist eine Adresse, wie in den Zeilen 24–26 gezeigt;
- Zeilen 8–9: direkte Initialisierung von zwei Attributen des Objekts;
- Zeile 17: Kopieren von Referenzen. Die Variablen obj1 und obj2 sind zwei Zeiger (Referenzen) auf dasselbe Objekt;
- Zeile 19: Wir ändern das Objekt, auf das [obj2] zeigt. Da [obj1] und [obj2] auf dasselbe Objekt zeigen, wird die Anzeige der Objekte [obj1, obj2] in den Zeilen 21 und 22 zeigen, dass sich das von [obj1] angezeigte Objekt geändert hat;
- Zeilen 24–26: Diese Zeilen sollen zeigen, dass die Variablen [obj1] und [obj2] gleich sind. Die Ausgabe von Zeile 26 wird dies bestätigen. Bei diesem Vergleich sind es die Adressen von [obj1] und [obj2], die gleich sind;
- Jedes Python-Objekt wird durch eine eindeutige ID identifiziert, die mit dem Ausdruck [id(object)] ermittelt wird. Die Zeilen 24 und 25 zeigen, dass die IDs der Objekte, auf die [obj1] und [obj2] verweisen, identisch sind, was belegt, dass diese beiden Referenzen auf dasselbe Objekt verweisen;
- Zeilen 27–29: Die Funktion [isinstance(expr, Type)] gibt den booleschen Wert True zurück, wenn der Ausdruck [expr] vom Typ [Type] ist. Hier werden wir sehen, dass [obj1] vom Typ [Object] ist, was naheliegend erscheint, aber auch vom Typ [object]. Die Klasse [object] ist die Oberklasse aller Python-Klassen. Aufgrund der Eigenschaft der Klassenvererbung besitzt eine Unterklasse F alle Eigenschaften ihrer Oberklasse P, und die Funktion [isinstance(instance of F, P)] gibt True zurück;
- Zeilen 30–32: zeigen, dass der Typ [int] ebenfalls ein Typ [object] ist. Alle Python-Typen leiten sich von der Klasse [object] ab;
Ergebnisse
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_01.py
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, <class '__main__.Objet'>,2595221838768,un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
objet2=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
True
type(obj1)=<class '__main__.Objet'>
isinstance(obj1,Objet)=True, isinstance(obj1,object)=True
type(4)=<class 'int'>
isinstance(4, int)=True, isinstance(4, object)=True
Process finished with exit code 0
12.2. Skript [classes_02]: Eine Klasse „Person“
Das Skript [classes_02] veranschaulicht, dass die Attribute einer Klasse öffentlich sind: Auf sie kann direkt von außerhalb der Klasse zugegriffen werden. Dies ist ein weiteres Beispiel für eine nicht empfohlene Verwendung von Klassen. Wir nehmen es jedoch auf, da Sie gelegentlich auf diese Art von Code stoßen können (Python erlaubt dies) und Sie in der Lage sein müssen, ihn zu verstehen.
Anmerkungen:
- Zeilen 2–9: eine Klasse mit einer Methode;
- Zeile 7: Jede Methode einer Klasse muss das Objekt `self`, das auf das aktuelle Objekt verweist, als ersten Parameter haben. Die Methode `[identity]` gibt eine Zeichenkette zurück;
- Zeile 15: Instanziierung eines [Person]-Objekts;
- Zeilen 16–19: zeigen, dass die Attribute des Objekts dynamisch erstellt werden können (sie sind in der Klassendefinition nicht vorhanden);
- Zeile 9: Die Attribute der Klasse werden durch die Notation [self.attribute] bezeichnet;
- Zeilen 23–24 zeigen, dass das Objekt [p] sowohl eine Instanz der Klasse [Person] als auch der Klasse [object] ist;
Ergebnisse
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_02.py
personne=[Paul,de la Hûche,48]
type(p)=<class '__main__.Personne'>
isinstance(Personne)=True, isinstance(object)=True
Process finished with exit code 0
12.3. Skript [classes_03]: Die Klasse „Person“ mit einem Konstruktor
Das Skript [classes_03] veranschaulicht die Standardverwendung einer Klasse:
Anmerkungen:
- Zeile 4: Der Klassenkonstruktor heißt __init__. Wie bei anderen Methoden ist sein erster Parameter self;
- Zeile 20: Ein Person-Objekt wird mithilfe des Klassenkonstruktors erstellt;
- Zeilen 13–15: Die Methode [identity] gibt eine Zeichenkette zurück, die den Inhalt des Objekts darstellt;
- Zeile 22: Zeigt die Identität der Person an;
Ergebnisse
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_03.py
personne=[Paul,de la Hûche,48]
Process finished with exit code 0
12.4. Skript [classes_04]: statische Methoden
Wir definieren die folgende [Utils]-Klasse (utils.py) im Ordner [modules]:

Anmerkungen
- Zeile 3: Die Annotation [@staticmethod] gibt an, dass die damit markierte Methode eine Klassenmethode und keine Instanzmethode ist. Dies geht daraus hervor, dass der erste Parameter der annotierten Methode nicht das Schlüsselwort [self] ist. Daher hat die statische Methode keinen Zugriff auf die Attribute des Objekts. Anstatt zu schreiben:
wir schreiben
Da wir oben [Utils.is_string_ok] geschrieben haben, wird die Methode [is_string_ok] als Klassenmethode bezeichnet (in diesem Fall die Klasse Utils). Um dies zu erreichen, muss die Methode [Utils.is_string_ok] mit dem Schlüsselwort [@staticmethod] versehen werden.
Die statische Methode [Utils.is_string_ok] ermöglicht es uns, hier zu überprüfen, ob ein Datenelement eine nicht leere Zeichenkette ist.
Das Skript [classes_04] verwendet die Klasse [Utils] wie folgt:
- Zeilen 1–4: Wir verwenden ein Konfigurationsskript;
Das Konfigurationsskript [config.py] lautet wie folgt:
- Zeile 9: Der Ordner [shared] wird dem Python-Pfad hinzugefügt;
Das Ergebnis der Ausführung lautet wie folgt:
12.5. Skript [classes_05]: Überprüfung der Gültigkeit von Attributen
Das Skript [classes_05] führt neue Konzepte ein:
- Definition eines benutzerdefinierten Ausnahmetyps;
- Definition der Methode [_str_], die die Standard-Identitätsmethode für Klassen darstellt;
- Definition von Eigenschaften;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
Anmerkungen:
- Zeilen 10–13: eine MyException-Klasse, die von der BaseException-Klasse abgeleitet ist (wir werden diesen Punkt etwas später behandeln). Sie fügt der letzteren keine Funktionalität hinzu. Sie dient lediglich dazu, eine benutzerdefinierte Ausnahme bereitzustellen;
- Zeile 19: Der Konstruktor hat Standardwerte für seine Parameter. Somit entspricht die Operation p = Person() der Operation p = Person("x", "y", 0);
- Zeilen 34–45: die Klassen-Properties. Dies sind Methoden, die mit dem Schlüsselwort [@property] annotiert sind. Sie dienen dazu, die Werte der Attribute festzulegen;
- Zeilen 47–77: die Setter der Klasse. Dies sind Methoden, die mit dem Schlüsselwort [@attributsetter] annotiert sind. Sie dienen dazu, den Wert der Attribute festzulegen;
- Zeilen 48–54: der Setter für das Attribut [first_name]. Diese Methode wird jedes Mal aufgerufen, wenn dem Attribut [first_name] ein Wert zugewiesen wird:
Zeile 2 löst den Aufruf [p.firstName(value)] aus. Der Vorteil der Verwendung eines Setters zur Zuweisung eines Werts an ein Attribut besteht darin, dass wir, da der Setter eine Funktion ist, die Gültigkeit des dem Attribut zugewiesenen Werts überprüfen können;
- Zeile 51: Wir überprüfen, ob der dem Attribut [first_name] zugewiesene Wert eine nicht leere Zeichenkette ist. Dazu verwenden wir die zuvor vorgestellte statische Methode [Utils.isStringOk];
- Zeile 52: Der dem Attribut [first_name] zugewiesene Wert wird um führende und nachfolgende Leerzeichen bereinigt und dem Attribut [self.__first_name] zugewiesen. Daher wird das Attribut [first_name] selbst hier nicht verwendet. Wir hätten es nicht anders machen können, sonst hätten wir einen unendlichen rekursiven Aufruf erhalten. Wir hätten jeden beliebigen Attributnamen verwenden können. Die Tatsache, dass wir das Attribut [__prénom] mit zwei Unterstrichen am Anfang des Bezeichners verwendet haben, hat eine besondere Bedeutung: Attribute, denen zwei Unterstriche vorangestellt sind, sind privat für die Klasse. Das bedeutet, dass sie von außerhalb der Klasse nicht sichtbar sind. Daher können wir nicht schreiben:
Tatsächlich werden wir gleich sehen, dass man das zwar schreiben kann, es aber den Vornamen nicht ändert. Es bewirkt etwas anderes;
- Zeilen 53–54: Wenn der Wert, der first_name zugewiesen wird, falsch ist, wird eine Ausnahme ausgelöst. Auf diese Weise weiß der aufrufende Code, dass sein Aufruf falsch ist;
- Zeilen 35–37: die Eigenschaft [first_name]. Sie wird jedes Mal aufgerufen, wenn [p.first_name] in einem Ausdruck geschrieben wird. Die Methode [p.first_name()] wird dann aufgerufen. Zeile 37: Wir geben den Wert des Attributs [__first_name] zurück, da wir gesehen haben, dass der Setter für das Attribut [first_name] dessen Wert dem privaten Attribut [__first_name] zuweist;
- Zeilen 56–62: Der Setter für das Attribut [last_name] ist ähnlich wie der für das Attribut [first_name] aufgebaut. Dasselbe gilt für den Setter für das Attribut [age] in den Zeilen 64–77;
- Obwohl die Eigenschaften [first_name, last_name, value] nicht die eigentlichen Attribute sind – diese lauten nämlich [__first_name, __last_name, __age] –, werden wir sie weiterhin als Klassenattribute bezeichnen, da sie als solche verwendet werden;
- Zeilen 19–28: Der Klassenkonstruktor verwendet implizit die Setter für die Attribute [first_name, last_name, age]. Tatsächlich wird durch das Schreiben von [self.first_name = first_name] in Zeile 26 implizit die Methode [first_name(self, first_name)] aufgerufen. Die Gültigkeit des Parameters [first_name] wird dann überprüft. Dasselbe gilt für die beiden anderen Attribute [last_name, age];
- mit diesem Modell können wir den Klassenattributen [first_name, last_name, age] keine falschen Werte zuweisen;
- Zeilen 30–32: Die Funktion __str__ ersetzt die zuvor als identity bezeichnete Methode. Der Name [__str__] (zwei Unterstriche vor und nach) ist nicht unbedeutend. Wir werden dies später sehen;
- Zeilen 83–86: Instanziierung einer Person, gefolgt von der Anzeige ihrer Identität;
- Zeile 84: Instanziierung;
- Zeile 86: Anzeige. Der Vorgang erfordert die Darstellung des Objekts p als Zeichenkette. Der Python-Interpreter ruft automatisch die Methode p.__str__() auf, sofern diese existiert. Diese Methode erfüllt denselben Zweck wie die Methode toString() in Java oder .NET-Sprachen;
- Zeilen 87–89: Behandlung einer möglichen MyException. Anschließend wird der Fehler angezeigt;
- Zeilen 91–99: wie oben für eine zweite Person, die mit falschen Parametern instanziiert wurde;
- Zeilen 102–109: wie oben für eine dritte Person, die mit Standardparametern instanziiert wurde: Es werden keine Parameter übergeben. Die Standardwerte dieser Parameter im Konstruktor werden dann hier verwendet;
- Zeilen 112–117: Wir haben gesagt, dass das Attribut [__first_name] privat ist und daher normalerweise von außerhalb der Klasse nicht zugänglich ist. Das wollen wir überprüfen;
- Zeilen 112–114: Wir weisen dem Attribut [__first_name] einen Wert zu und überprüfen dann die Werte der Attribute [__first_name] und [first_name], die normalerweise identisch sein sollten;
- Zeilen 115–117: Wir wiederholen den Vorgang und initialisieren diesmal das Attribut [first_name];
Ergebnisse
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_05.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
personne=[x,y,0]
p.prénom=x
p.__prénom=Gaëlle
p.prénom=Sébastien
p.__prénom=Gaëlle
Process finished with exit code 0
Anmerkungen
- Zeilen 5–6: Wir sehen, dass die Zuweisung [p.__first_name = "Gaëlle"] den Wert des Attributs [first_name] nicht geändert hat, Zeile 5;
- Zeilen 7–8: Wir sehen, dass die Zuweisung [p.first_name = "Sébastien"] den Wert des Attributs [__first_name] nicht geändert hat, Zeile 8;
Was können wir daraus schließen? Dass die Operation [p.__first_name = "Gaëlle"] wahrscheinlich ein öffentliches Attribut [__first_name] für die Klasse erstellt hat, dass sich dieses jedoch von dem privaten Attribut [__first_name] unterscheidet, das darin manipuliert wird;
12.6. Skript [classes_06]: Hinzufügen einer Objektinitialisierungsmethode
Das Skript [classes_06] fügt der Klasse [Person] eine Methode hinzu:
Anmerkungen:
- Der Unterschied zum vorherigen Skript liegt in den Zeilen 30–33. Wir haben die Methode initWithPerson hinzugefügt. Diese Methode ruft den Konstruktor __init__ auf. Anders als in typisierten Sprachen ist es nicht möglich, Methoden mit demselben Namen zu haben, die sich durch die Art ihrer Parameter oder ihrer Rückgabewerte unterscheiden. Daher ist es nicht möglich, mehrere Konstruktoren zu haben, die das Objekt aus unterschiedlichen Parametern erstellen würden, in diesem Fall ein Objekt vom Typ Person;
Ergebnisse
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_06.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
p=[x,y,0]
p2=[x,y,0]
Process finished with exit code 0
12.7. Skript [classes_07]: eine Liste von Person-Objekten
Wir werden nun die Klassen [MyException] und [Person] in einem Modul ablegen, damit wir sie verwenden können, ohne ihren Code kopieren zu müssen:

Beide Klassen befinden sich im oben genannten Modul [myclasses.py].
Das Skript [classes_07] zeigt, dass wir eine Liste von Objekten haben können:
Anmerkungen:
- Zeile 7: Wir importieren die Klasse [Person];
- Zeile 11: eine Liste von Objekten vom Typ [Person];
- Zeilen 13–14: Wir durchlaufen diese Liste, um jedes ihrer Elemente anzuzeigen;
- Zeile 14: Die Funktion [print] gibt die Zeichenkette aus, die das Objekt [group[i]] repräsentiert. Standardmäßig wird die Methode [__str__] dieser Objekte aufgerufen;
Ergebnisse
C:\Users\serge\.virtualenvs\cours-python-v02\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/v-02/classes/01/classes_07.py
groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]
Process finished with exit code 0
12.8. Skript [classes_08]: Erstellen einer von der Klasse „Person“ abgeleiteten Klasse
Wir definieren die folgende Klasse [Teacher] im Modul [myclasses]:
# classe Enseignant
class Enseignant(Personne):
# constructeur
def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
# prénom : prénom de la personne
# nom : nom de la personne
# âge : âge de la personne
# discipline : discpline enseignée
# initialisation du parent
Personne.__init__(self, prénom, nom, âge)
# autres initialisations
self.discipline = discipline
# toString
def __str__(self) -> str:
return f"enseignant[{super().__str__()},{self.discipline}]"
# propriétés
@property
def discipline(self) -> str:
return self.__discipline
@discipline.setter
def discipline(self, discipline: str):
# la discipline doit être une chaîne non vide
if Utils.is_string_ok(discipline):
self.__discipline = discipline
else:
raise MyException("La discipline doit être une chaîne de caractères non vide")
- Zeile 2: deklariert die Klasse „Teacher“ als Unterklasse der Klasse „Person“. Eine Unterklasse verfügt über alle Eigenschaften (Attribute und Methoden) ihrer übergeordneten Klasse sowie über eigene;
- Zeile 13: Die Klasse [Teacher] definiert ein neues Attribut [subject];
- Zeile 11: Der Konstruktor der abgeleiteten Klasse „Teacher“ muss den Konstruktor der übergeordneten Klasse „Person“ aufrufen und ihm die erwarteten Parameter übergeben;
- Zeile 17: Die Funktion [super()] ruft die übergeordnete Klasse auf. Hier rufen wir die Funktion [__str__] der übergeordneten Klasse auf;
- Zeilen 19–30: Der Getter und der Setter für das neue Attribut [subject] werden definiert;
Das Skript [classes_08] verwendet die Klasse [Teacher] wie folgt:
Anmerkungen:
- Zeile 7: Wir importieren die Klassen [Person] und [Teacher], die in der Datei [myclasses.py] definiert sind;
- Zeilen 11–14: Wir definieren eine Gruppe von Personen und zeigen dann deren Identitäten an;
Ergebnisse
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_08.py
groupe[0]=enseignant[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
Process finished with exit code 0
12.9. Skript [classes_09]: zweite Klasse, abgeleitet von der Klasse Person
Das Skript [classes_09] führt die Klasse [Student] ein, die von der Klasse [Person] abgeleitet ist. Diese ist im Modul [myclasses] wie folgt definiert:
# classe Etudiant
class Etudiant(Personne):
# constructeur
def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0, formation: str = "x"):
Personne.__init__(self, prénom, nom, âge)
self.formation = formation
# toString
def __str__(self: object) -> str:
return f"étudiant[{super().__str__()},{self.formation}]"
# propriétés
@property
def formation(self) -> str:
return self.__formation
@formation.setter
def formation(self, formation: str):
# la formation doit être une chaîne non vide
if Utils.is_string_ok(formation):
self.__formation = formation
else:
raise MyException("La formation doit être une chaîne de caractères non vide")
Das Skript [classes_09] verwendet die Klasse [Student] wie folgt:
Anmerkungen:
- Dieses Skript ähnelt dem vorherigen.
Ergebnisse
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_09.py
enseignant[[Paul,Langevin,48],anglais]
[Sylvie,Lefur,70]
étudiant[[Steve,Boer,22],iup2 qualité]
Process finished with exit code 0
12.10. Skript [classes_10]: die Eigenschaft [__dict__]
Das Skript [classes_10] stellt die Eigenschaft [__dict__] vor, die wir später häufig verwenden werden:
Kommentare
- Zeilen 1–4: Die Anwendung wird eingerichtet;
- Zeile 7: Die Klasse [Student] wird importiert;
- Zeile 11: Instanziierung eines Studenten;
- Zeile 13: Verwendung der vordefinierten Methode [__dict__] (zwei Unterstriche vor und nach dem Bezeichner);
Die Ergebnisse lauten wie folgt:
- In Zeile 2 erhalten wir ein Wörterbuch, dessen Schlüssel die Eigenschaften des Objekts sind, denen der Name der Klasse vorangestellt ist, zu der sie gehören. Wir werden dieses Wörterbuch verwenden, um eine Brücke zwischen dem Objekt und dem Wörterbuch zu schlagen;