Skip to content

12. Klassen und Objekte

Die Klasse ist die Vorlage, nach der Objekte erstellt werden. Man sagt, dass ein Objekt eine Instanz einer Klasse ist.

Image

Hinweis: Der Ordner „[shared]“ wurde im Projektordner „[Sources Root]“ abgelegt.

12.1. Skript [classes_01]: eine Klasse „Objet“

Das Skript [classes_01] zeigt eine veraltete Verwendung von Klassen:


# eine Klasse
class Objet(object):
    """une classe Objet vide"""


# Jede Variable vom Typ [Objet] kann konstruktionsbedingt Attribute haben
obj1 = Objet()
obj1.attr1 = "un"
obj1.attr2 = 100
# zeigt das Objekt an
print(f"objet1=[{obj1}, {type(obj1)},{id(obj1)},{obj1.attr1},{obj1.attr2}]")
# ändert das Objekt
obj1.attr2 += 100
# zeigt das Objekt an
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
# weist die Referenz obj1 dem Objekt obj2 zu
obj2 = obj1
# ändert das Objekt, auf das obj2 verweist
obj2.attr2 = 0
# zeigt beide Objekte an – obj1 verweist nun auf ein geändertes Objekt
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2.attr1},{obj2.attr2}]")
# obj1 und obj2 verweisen auf dasselbe Objekt
print(f"objet1=[{obj1}, {id(obj1)},{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2}, {id(obj2)},{obj2.attr1},{obj2.attr2}]")
print(obj1 == obj2)
# Typ der Instanz obj1
print(f"type(obj1)={type(obj1)}")
print(f"isinstance(obj1,Objet)={isinstance(obj1, Objet)}, isinstance(obj1,object)={isinstance(obj1, object)}")
# Jeder Typ ist in Python ein Objekt
print(f"type(4)={type(4)}")
print(f"isinstance(4, int)={isinstance(4, int)}, isinstance(4, object)={isinstance(4, object)}")

Anmerkungen:

  • Zeilen 2–3: eine leere Klasse [Objet];
  • Zeile 2: Die Klasse kann in drei Formen deklariert werden:
    • class Objekt;
    • class Objekt();
    • class Objekt(object);
  • Zeile 3: eine weitere Form des Kommentars. Dieser, dem drei „ vorangestellt sind, kann sich über mehrere Zeilen erstrecken;
  • Zeile 7: Instanziierung der Klasse Objet. Das Ergebnis ist eine Adresse, wie die Zeilen 24–26 zeigen;
  • 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: Das Objekt, auf das [obj2] verweist, wird geändert. Da [obj1] und [obj2] auf dasselbe Objekt verweisen, zeigen die Ausgabebeispiele für das Objekt [obj1, obj2] in den Zeilen 21 und 22, dass sich das Objekt, auf das [obj1] verweist, geändert hat;
  • Zeilen 24–26: Diese Zeilen sollen die Gleichheit der Variablen [obj1] und [obj2] zeigen. Die Ausgabe von Zeile 26 wird dies zeigen. Bei diesem Vergleich sind die Adressen [obj1] und [obj2] gleich;
  • jedes Python-Objekt wird durch eine eindeutige Nummer identifiziert, die man mit dem Ausdruck [id(objet)] erhält. Die Zeilen 24 und 25 zeigen, dass die Nummern 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 [Objet] ist, was naheliegend erscheint, aber auch vom Typ [object]. Die Klasse [object] ist die übergeordnete Klasse aller Python-Klassen. Aufgrund der Eigenschaften der Klassenvererbung verfügt eine untergeordnete Klasse F über alle Eigenschaften ihrer übergeordneten Klasse P, und die Funktion [isinstance(instance de 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] zeigt, dass die Attribute einer Klasse öffentlich sind: Sie sind direkt von außerhalb der Klasse zugänglich. Auch hier handelt es sich um eine nicht empfohlene Verwendung von Klassen. Wir führen sie dennoch auf, da man diese Art von Code manchmal antrifft (Python akzeptiert ihn) und man ihn dann verstehen können muss.


# Klasse „Person“
class Personne:
    # Attribute der Klasse
    # nicht deklariert – können dynamisch erstellt werden

    # Methode
    def identité(self: object) -> str:
        # verwendet offenbar nicht vorhandene Attribute
        return f"[{self.prénom},{self.nom},{self.âge}]"


# ---------------------------------- main
# Die Attribute sind öffentlich und können dynamisch angelegt werden
# Klasseninstanziierung
p = Personne()
# direkte Initialisierung von Klassenattributen
p.prénom = "Paul"
p.nom = "de la Hûche"
p.âge = 48
# Aufruf einer Methode der Klasse
print(f"personne={p.identité()}")
# Typ der Instanz p
print(f"type(p)={type(p)}")
print(f"isinstance(Personne)={isinstance(p, Personne)}, isinstance(object)={isinstance(p, object)}")

Anmerkungen:

  • Zeilen 2–9: eine Klasse mit einer Methode;
  • Zeile 7: Jede Methode einer Klasse muss als ersten Parameter das Objekt self haben, das das aktuelle Objekt bezeichnet. Die Methode [identité] gibt eine Zeichenkette zurück;
  • Zeile 15: Instanziierung eines Objekts [Personne];
  • Zeilen 16–19: zeigen, dass die Attribute des Objekts dynamisch angelegt werden können (sie sind in der Klassendefinition nicht vorhanden);
  • Zeile 9: Die Attribute der Klasse werden durch die Notation [self.attribut] bezeichnet;
  • Die Zeilen 23–24 zeigen, dass das Objekt [p] sowohl eine Instanz der Klasse [Personne] 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 normale Verwendung einer Klasse:


# Klasse „Person“
class Personne:
    # Konstruktor – initialisiert drei Attribute
    def __init__(self: object, prénom: str, nom: str, âge: int):
        # Vorname: Vorname der Person
        # Nachname: Nachname der Person
        # Alter: Alter der Person

        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    # gibt die Attribute der Klasse als Zeichenkette zurück
    def identité(self: object) -> str:
        return f"[{self.prénom},{self.nom},{self.âge}]"


# ---------------------------------- Hand
# ein Objekt vom Typ „Person“
p = Personne("Paul", "de la Hûche", 48)
# Aufruf einer Methode
print(f"personne={p.identité()}")

Hinweise:

  • Zeile 4: Der Konstruktor einer Klasse heißt __init__. Wie bei den anderen Methoden ist sein erster Parameter „self“;
  • Zeile 20: Ein Objekt Personne wird mit dem Konstruktor der Klasse erstellt;
  • Zeilen 13–15: Die Methode [identité] gibt eine Zeichenkette zurück, die den Inhalt des Objekts darstellt;
  • Zeile 22: Anzeige der Identität der 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_03.py
personne=[Paul,de la Hûche,48]

Process finished with exit code 0

12.4. Skript [classes_04]: statische Methoden

Wir definieren im Ordner [modules] die folgende Klasse [Utils] (utils.py):

Image


class Utils:
    # statische Methode
    @staticmethod
    def is_string_ok(string: str) -> bool:
        # Ist „string“ eine Zeichenkette?
        erreur = not isinstance(string, str)
        if not erreur:
            # Ist die Zeichenkette leer?
            erreur = string.strip() == ''
        # Ergebnis
        return not erreur

Anmerkungen

  • Zeile 3: Die Annotation [@staticmethod] gibt an, dass es sich bei der so annotierten Methode um eine Klassenmethode und nicht um eine Instanzmethode handelt. Dies lässt sich daran erkennen, dass der erste Parameter der so annotierten Methode nicht das Schlüsselwort [self] ist. Daher hat die statische Methode keinen Zugriff auf die Attribute des Objekts. Anstatt zu schreiben:
u=Utils()
print(u.is_string_ok("abcd")

schreibt man

print(Utils.is_string_ok("abcd")

Da oben [Utils.is_string_ok] geschrieben wurde, wird die Methode [is_string_ok] als Klassenmethode bezeichnet (hier die Klasse „Utils“). Dazu muss die Methode [Utils.is_string_ok] mit dem Schlüsselwort [@staticmethod] annotiert werden.

Die statische Methode [Utils.is_string_ok] ermöglicht es hier, zu überprüfen, ob eine Angabe eine nicht leere Zeichenkette ist.

Das Skript [classes_04] verwendet die Klasse [Utils] wie folgt:


# Die Anwendung wird konfiguriert
import config

config = config.configure()

# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from utilitaires import Utils

print(Utils.is_string_ok("  "))
print(Utils.is_string_ok(47))
print(Utils.is_string_ok(" q "))
  • Zeilen 1–4: Es wird ein Konfigurationsskript verwendet;

Das Konfigurationsskript [config.py] lautet wie folgt:


def configure():
    import os

    # Verzeichnis der Konfigurationsdatei
    script_dir = os.path.dirname(os.path.abspath(__file__))

    # Absolute Pfade der Ordner, die in den Syspath aufgenommen werden sollen
    absolute_dependencies = [
        f"{script_dir}/shared",
        "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages",
        "C:/myprograms/Python38/lib",
        "C:/myprograms/Python38/DLLs"
    ]

    # Aktualisierung des Syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # Die Konfiguration wird zurückgegeben
    return {}
  • Zeile 9: Der Ordner „[shared]“ wird in den Python-Pfad aufgenommen;

Das Ergebnis der Ausführung lautet wie folgt:

1
2
3
4
5
6
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_04.py
False
False
True

Process finished with exit code 0

12.5. Skript [classes_05]: Gültigkeitsprüfungen der Attribute

Das Skript [classes_05] führt neue Konzepte ein:

  • Definition eines proprietären Ausnahmetyps;
  • Definition der Methode [_str_], die als Standard-Identitätsmethode der Klassen dient;
  • Definition von Eigenschaften;

# Die Anwendung wird konfiguriert
import config

config = config.configure()

# Der Syspath ist konfiguriert – die Importe können nun durchgeführt werden
from utilitaires import Utils


# Eine benutzerdefinierte Ausnahmeklasse, die von [BaseException] abgeleitet ist
class MyException(BaseException):
    # Es wird nichts unternommen: leere Klasse
    pass


# Klasse „Person“
class Personne:
    # Konstruktor
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        # Vorname: Vorname der Person
        # Nachname: Nachname der Person
        # Alter: Alter der Person

        # Speicherung der Parameter
        # Die Initialisierungen erfolgen über die Setter
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    # Methode toString der Klasse
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    # Getter
    @property
    def prénom(self) -> str:
        return self.__prénom

    @property
    def nom(self) -> str:
        return self.__nom

    @property
    def âge(self) -> int:
        return self.__âge

    # Setter
    @prénom.setter
    def prénom(self, prénom: str):
        # Der Vorname darf nicht leer sein
        if Utils.is_string_ok(prénom):
            self.__prénom = prénom.strip()
        else:
            raise MyException("Le prénom doit être une chaîne de caractères non vide")

    @nom.setter
    def nom(self, nom: str):
        # Der Vorname darf nicht leer sein
        if Utils.is_string_ok(nom):
            self.__nom = nom.strip()
        else:
            raise MyException("Le nom doit être une chaîne de caractères non vide")

    @âge.setter
    def âge(self, âge: int):
        # Das Alter muss eine ganze Zahl >= 0 sein
        erreur = False
        if isinstance(âge, int):
            if âge >= 0:
                self.__âge = âge
            else:
                erreur = True
        else:
            erreur = True
        # Fehler?
        if erreur:
            raise MyException("L'âge doit être un entier >=0")


# ---------------------------------- main
# ein Objekt vom Typ „Person“
try:
    # Instanziierung der Klasse „Person“
    p = Personne("Paul", "de la Hûche", 48)
    # Anzeige des Objekts p
    print(f"personne={p}")
except MyException as erreur:
    # Anzeige des Fehlers
    print(erreur)

# ein weiteres Objekt der Klasse „Person“
try:
    # Instanziierung der Klasse „Person“
    p = Personne("xx", "yy", "zz")
    # Anzeige des Objekts p
    print(f"personne={p}")
except MyException as erreur:
    # Fehleranzeige
    print(erreur)

# eine weitere Person, diesmal ohne Parameter
try:
    # Instanziierung der Klasse „Person“
    p = Personne()
    # Anzeige des Objekts p
    print(f"personne={p}")
except MyException as erreur:
    # Anzeige der Fehlermeldung
    print(erreur)

# Auf die privaten Attribute __attr der Klasse kann nicht zugegriffen werden
p.__prénom = "Gaëlle"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")
p.prénom = "Sébastien"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")

Hinweise:

  • Zeilen 10–13: Eine Klasse MyException, die von der Klasse BaseException abgeleitet ist (darauf gehen wir etwas später ein). Sie fügt dieser keine neuen Funktionen hinzu. Sie dient lediglich dazu, eine benutzerdefinierte Ausnahme zu haben;
  • Zeile 19: Der Konstruktor verfügt über Standardwerte für seine Parameter. Somit entspricht die Operation p=Personne() der Operation p=Personne("x","y",0);
  • Zeilen 34–45: Die Eigenschaften der Klasse. Dabei handelt es sich um Methoden, die mit dem Schlüsselwort [@property] annotiert sind. Sie dienen dazu, den Wert der Attribute zurückzugeben;
  • Zeilen 47–77: die Setter-Methoden der Klasse. Es handelt sich um 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 [prénom]. Diese Methode wird jedes Mal aufgerufen, wenn dem Attribut [prénom] ein Wert zugewiesen wird:
p=Personne(…)
p.prénom=valeur

Zeile 2 löst den Aufruf von [p.prénom(valeur)] aus. Der Vorteil der Verwendung eines Setters zum Zuweisen eines Werts an ein Attribut besteht darin, dass der Setter eine Funktion ist und somit die Gültigkeit des dem Attribut zugewiesenen Werts überprüft werden kann;

  • Zeile 51: Es wird überprüft, ob der dem Attribut [prénom] zugewiesene Wert eine nicht leere Zeichenkette ist. Dazu wird die zuvor beschriebene statische Methode [Utils.isStringOk] verwendet;
  • Zeile 52: Der dem Attribut [prénom] zugewiesene Wert wird um die „Leerzeichen“ am Anfang und Ende der Zeichenkette bereinigt und dem Attribut [self.__prénom] zugewiesen. Es wird hier also nicht das Attribut [prénom] verwendet. Das wäre auch nicht möglich gewesen, da es sonst zu einem unendlichen rekursiven Aufruf gekommen wäre. Man hätte jeden beliebigen Attributnamen verwenden können. Die Verwendung des Attributs [__prénom] mit zwei Unterstrichen am Anfang des Bezeichners hat eine besondere Bedeutung: Attribute, denen zwei Unterstriche vorangestellt sind, sind klassenintern. Das bedeutet, dass sie von außerhalb der Klasse nicht sichtbar sind. Man kann daher nicht schreiben:
p=Personne(…)
p.__prénom=valeur

Tatsächlich werden wir bald sehen, dass man dies zwar schreiben kann, es den Vornamen jedoch nicht ändert. Es bewirkt etwas anderes;

  • Zeilen 53–54: Wenn der dem Vornamen zugewiesene Wert nicht korrekt ist, wird eine Ausnahme ausgelöst. So weiß der aufrufende Code, dass sein Aufruf fehlerhaft ist;
  • Zeilen 35–37: die Eigenschaft [prénom]. Sie wird jedes Mal aufgerufen, wenn man [p.prénom] in einem Ausdruck schreibt. Dann wird die Methode [p.prénom()] aufgerufen. In Zeile 37 wird der Wert des Attributs [__prénom] zurückgegeben, da wir gesehen haben, dass der Setter des Attributs [prénom] dessen Wert dem privaten Attribut [__prénom] zuweist;
  • Zeilen 56–62: Der Setter des Attributs [nom] ist analog zu dem des Attributs [prénom] aufgebaut. Das Gleiche gilt für den Setter des Attributs [âge] in den Zeilen 64–77;
  • obwohl die Eigenschaften [prénom, nom, valeur] nicht die eigentlichen Attribute sind – diese lauten in Wirklichkeit [__prénom, __nom, __âge] –, werden wir sie weiterhin als Attribute der Klasse bezeichnen, da sie als solche verwendet werden;
  • Zeilen 19–28: Der Konstruktor der Klasse verwendet implizit die Setter der Attribute [prénom, nom, âge]. Wenn nämlich in Zeile 26 „[self.prénom = prénom]“ geschrieben wird, wird implizit die Methode „[prénom(self, prénom)]“ aufgerufen. Die Gültigkeit des Parameters [prénom] wird dann überprüft. Dasselbe gilt für die beiden anderen Attribute [nom, âge];
  • bei diesem Modell können den Attributen [prénom, nom, âge] der Klasse keine falschen Werte zugewiesen werden;
  • Zeilen 30–32: Die Funktion __str__ ersetzt die Methode, die zuvor identité hieß. Der Name [__str__] (zwei Unterstriche vor und hinter dem Namen) ist nicht zufällig gewählt. Das werden wir im Folgenden sehen;
  • Zeilen 83–86: Instanziierung einer Person und anschließende Anzeige ihrer Identität;
  • Zeile 84: Instanziierung;
  • Zeile 86: Anzeige. Der Vorgang fordert an, die Person p in Form einer Zeichenkette anzuzeigen. Der Python-Interpreter ruft dann automatisch die Methode p.__str__() auf, sofern diese existiert. Diese Methode erfüllt dieselbe Funktion wie die Methode toString() in Java oder in den Sprachen .NET;
  • Zeilen 87–89: Behandlung einer möglichen Ausnahme vom Typ MyException. Zeigt dann den Fehler an;
  • Zeilen 91–99: Gleiches gilt für eine zweite Person, die mit falschen Parametern instanziiert wurde;
  • Zeilen 102–109: Gleiches gilt für eine dritte Person, die mit den Standardparametern instanziiert wird: Es werden keine Parameter übergeben. Hier werden also die Standardwerte dieser Parameter im Konstruktor verwendet;
  • Zeilen 112–117: Wir haben festgelegt, dass das Attribut [__prénom] privat ist und daher normalerweise von außerhalb der Klasse nicht zugänglich ist. Dies wollen wir überprüfen;
  • Zeilen 112–114: Dem Attribut [__prénom] wird ein Wert zugewiesen, anschließend wird der Wert der Attribute [__prénom] und [prénom] überprüft, die normalerweise identisch sind;
  • Zeilen 115–117: Der Vorgang wird wiederholt, wobei diesmal das Attribut [prénom] initialisiert wird;

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: Es ist zu erkennen, dass die Zuweisung [p.__prénom = "Gaëlle"] den Wert des Attributs [prénom] (Zeile 5) nicht verändert hat;
  • Zeilen 7–8: Es ist ersichtlich, dass die Zuweisung [p.prénom = "Sébastien"] den Wert des Attributs [__prénom] nicht verändert hat, Zeile 8;

Was lässt sich daraus schließen? Dass die Operation [p.__prénom = "Gaëlle"] wahrscheinlich ein öffentliches Attribut [__prénom] für die Klasse angelegt hat, dieses sich jedoch von dem privaten Attribut [__prénom] unterscheidet, das innerhalb der Klasse bearbeitet wurde;

12.6. Skript [classes_06]: Hinzufügen einer Methode zur Initialisierung des Objekts

Das Skript [classes_06] fügt der Klasse [Personne] eine Methode hinzu:


# Die Anwendung wird konfiguriert
import config

config = config.configure()

# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from utilitaires import Utils


# eine benutzerdefinierte Ausnahmeklasse, die von [BaseException] abgeleitet ist
class MyException(BaseException):
    # Es wird nichts unternommen: leere Klasse
    pass


# Klasse „Person“
class Personne:
    # Konstruktor
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        # Vorname: Vorname der Person
        # Nachname: Nachname der Person
        # Alter: Alter der Person

        # Speicherung der Parameter
        # Die Initialisierung erfolgt über die Setter
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    # andere Initialisierungsmethode
    def init_with_personne(self: object, p: object):
        # initialisiert das aktuelle Objekt mit einer Person p
        self.__init__(p.prénom, p.nom, p.âge)

    # Methode toString der Klasse
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    # Getter


    # Setter



# ---------------------------------- main
# ein Objekt der Klasse „Person“
try:
    # Instanziierung der Klasse „Person“
    p = Personne("Paul", "de la Hûche", 48)
    # Anzeige des Objekts p
    print(f"personne={p}")
except MyException as erreur:
    # Anzeige der Fehlermeldung
    print(erreur)

# ein weiteres Objekt der Klasse „Person“
try:
    # Instanziierung der Klasse „Person“
    p = Personne("xx", "yy", "zz")
    # Anzeige des Objekts p
    print(f"p={p}")
except MyException as erreur:
    # Anzeige einer Fehlermeldung
    print(erreur)

# eine weitere Person, diesmal ohne Parameter
try:
    # Instanziierung der Klasse „Person“
    p = Personne()
    # Anzeige des Objekts p
    print(f"p={p}")
except MyException as erreur:
    # Anzeige einer Fehlermeldung
    print(erreur)

# eine weitere Person, die durch Kopieren erstellt wurde
try:
    # Instanziierung der Klasse „Person“
    p2 = Personne()
    p2.init_with_personne(p)
    # Anzeige des Objekts p2
    print(f"p2={p2}")
except MyException as erreur:
    # Anzeige einer Fehlermeldung
    print(erreur)

Anmerkungen:

  • Der Unterschied zum vorherigen Skript liegt in den Zeilen 30–33. Die Methode initWithPersonne wurde hinzugefügt. Diese ruft den Konstruktor __init__ auf. Es gibt keine Möglichkeit, wie in typisierten Sprachen Methoden mit demselben Namen zu haben, die sich durch die Art ihrer Parameter oder ihres Ergebnisses unterscheiden. Es ist daher nicht möglich, mehrere Konstruktoren zu haben, die das Objekt anhand unterschiedlicher Parameter erstellen würden, in diesem Fall ein Objekt vom Typ Personne;

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 fügen nun die Klassen [MyException] und [Personne] in ein Modul ein, um sie nutzen zu können, ohne ihren Code erneut eingeben zu müssen:

Image

Beide Klassen befinden sich im oben genannten Modul [myclasses.py].

Das Skript [classes_07] zeigt, dass man eine Liste von Objekten erstellen kann:


# Die Anwendung wird konfiguriert
import config

config = config.configure()

# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from myclasses import Personne

# ---------------------------------- main
# Erstellung einer Liste von Personenobjekten
groupe = [Personne("Paul", "Langevin", 48), Personne("Sylvie", "Lefur", 70)]
# Identität dieser Personen
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

Hinweise:

  • Zeile 7: Die Klasse [Personne] wird importiert;
  • Zeile 11: eine Liste von Objekten vom Typ [Personne];
  • Zeilen 13–14: Diese Liste wird durchlaufen, um jedes ihrer Elemente anzuzeigen;
  • Zeile 14: Die Funktion [print] gibt die Zeichenkette aus, die das Objekt [groupe[i]] darstellt. Standardmäßig wird hierfür die Methode [__str__] 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]: Erstellung einer von der Klasse „Person“ abgeleiteten Klasse

Im Modul [myclasses] definieren wir die folgende Klasse [Enseignant]:


# Klasse „Lehrer“
class Enseignant(Personne):
    # Konstruktor
    def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
        # Vorname: Vorname der Person
        # Nachname: Nachname der Person
        # Alter: Alter der Person
        # Fach: unterrichtetes Fach

        # Initialisierung des übergeordneten Elements
        Personne.__init__(self, prénom, nom, âge)
        # Sonstige Initialen
        self.discipline = discipline

    # toString
    def __str__(self) -> str:
        return f"enseignant[{super().__str__()},{self.discipline}]"

    # Eigenschaften
    @property
    def discipline(self) -> str:
        return self.__discipline

    @discipline.setter
    def discipline(self, discipline: str):
        # Das Fach muss eine nicht leere Zeichenkette sein
        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 „Enseignant“ als abgeleitete Klasse der Klasse „Personne“. Eine abgeleitete Klasse verfügt über alle Eigenschaften (Attribute und Methoden) ihrer übergeordneten Klasse sowie über ihre eigenen;
  • Zeile 13: Die Klasse [Enseignant] definiert ein neues Attribut [discipline];
  • Zeile 11: Der Konstruktor der abgeleiteten Klasse Enseignant muss den Konstruktor der übergeordneten Klasse Personne aufrufen und ihm die erwarteten Parameter übergeben;
  • Zeile 17: Die Funktion [super()] gibt die übergeordnete Klasse zurück. Hier wird die Funktion [__str__] der übergeordneten Klasse aufgerufen;
  • Zeilen 19–30: Hier werden der Getter und der Setter des neuen Attributs [discipline] definiert;

Das Skript [classes_08] verwendet die Klasse [Enseignant] wie folgt:


# Die Anwendung wird konfiguriert
import config

config = config.configure()

# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from myclasses import Personne, Enseignant

# ---------------------------------- main
# Erstellung eines Arrays mit Objekten vom Typ „Person“ und abgeleiteten Typen
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70)]
# Identität dieser Personen
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

Hinweise:

  • Zeile 7: Die in der Datei [myclasses.py] definierten Klassen [Personne] und [Enseignant] werden importiert;
  • Zeilen 11–14: Es wird eine Personengruppe definiert, deren Identität anschließend angezeigt wird;

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 von der Klasse „Person“ abgeleitete Klasse

Das Skript [classes_09] führt die Klasse [Etudiant] ein, die von der Klasse [Personne] abgeleitet ist. Diese ist im Modul [myclasses] wie folgt definiert:


# Klasse „Student“
class Etudiant(Personne):
    # Konstruktor
    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}]"

    # Eigenschaften
    @property
    def formation(self) -> str:
        return self.__formation

    @formation.setter
    def formation(self, formation: str):
        # Die Ausbildung muss eine nicht leere Zeichenfolge sein
        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 [Etudiant] wie folgt:


# Die Anwendung wird konfiguriert
import config

config = config.configure()

# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from myclasses import Personne, Enseignant, Etudiant

# ---------------------------------- main
# Erstellung eines Arrays mit Objekten vom Typ „Person“ und abgeleiteten Typen
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70),
          Etudiant("Steve", "Boer", 22, "iup2 qualité")]
# Identität dieser Personen
for personne in groupe:
    # Anzeige der Person
    print(personne)

Hinweise:

  • Dieses Skript entspricht 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] enthält die Eigenschaft [__dict__], die wir im weiteren Verlauf häufig verwenden werden:


# Konfiguration der Anwendung
import config

config = config.configure()

# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from myclasses import Etudiant

# ---------------------------------- main
# Anlegen eines Studenten
étudiant=Etudiant("Steve", "Boer", 22, "iup2 qualité")
# Eigenschaftswörterbuch
print(étudiant.__dict__)

Kommentare

  • Zeilen 1–4: Die Anwendung wird konfiguriert;
  • Zeile 7: Die Klasse [Etudiant] wird importiert;
  • Zeile 11: Instanziierung eines Studenten;
  • Zeile 13: Verwendung der vordefinierten Methode [__dict__] (2 unterstrichene Zeichen vor und nach der Kennung);

Die Ergebnisse lauten wie folgt:

1
2
3
4
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_10.py
{'_Personne__prénom': 'Steve', '_Personne__nom': 'Boer', '_Personne__âge': 22, '_Etudiant__formation': 'iup2 qualité'}

Process finished with exit code 0
  • 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 Objekt und Wörterbuch zu schlagen;