Skip to content

12. Klassen und Objekte

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

Image

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:

#  a class
class Objet(object):
    """une classe Objet vide"""


#  any variable of type [Object] can have attributes by construction
obj1 = Objet()
obj1.attr1 = "un"
obj1.attr2 = 100
#  displays the
print(f"objet1=[{obj1}, {type(obj1)},{id(obj1)},{obj1.attr1},{obj1.attr2}]")
#  modify object
obj1.attr2 += 100
#  displays the
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
#  assign reference obj1 to obj2
obj2 = obj1
#  modifies the object pointed to by obj2
obj2.attr2 = 0
#  displays both objects - obj1 now points to a modified object
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2.attr1},{obj2.attr2}]")
#  obj1 and obj2 point to the same object
print(f"objet1=[{obj1}, {id(obj1)},{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2}, {id(obj2)},{obj2.attr1},{obj2.attr2}]")
print(obj1 == obj2)
#  instance type obj1
print(f"type(obj1)={type(obj1)}")
print(f"isinstance(obj1,Objet)={isinstance(obj1, Objet)}, isinstance(obj1,object)={isinstance(obj1, object)}")
#  any type is an object in Python
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 [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.

#  person class
class Personne:
    #  class attributes
    #  undeclared - can be created dynamically

    #  method
    def identité(self: object) -> str:
        #  a priori, uses non-existent attributes
        return f"[{self.prénom},{self.nom},{self.âge}]"


#  ---------------------------------- main
#  attributes are public and can be created dynamically
#  class instantiation
p = Personne()
#  direct initialization of class attributes
p.prénom = "Paul"
p.nom = "de la Hûche"
p.âge = 48
#  call a method of the
print(f"personne={p.identité()}")
#  instance type 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 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:

#  person class
class Personne:
    #  builder - initializes three attributes
    def __init__(self: object, prénom: str, nom: str, âge: int):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

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

    #  renders class attributes in string form
    def identité(self: object) -> str:
        return f"[{self.prénom},{self.nom},{self.âge}]"


#  ---------------------------------- main
#  a Person object
p = Personne("Paul", "de la Hûche", 48)
#  method call
print(f"personne={p.identité()}")

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]:

Image

class Utils:
    #  static method
    @staticmethod
    def is_string_ok(string: str) -> bool:
        #  string is a string
        erreur = not isinstance(string, str)
        if not erreur:
            #  is the chain empty?
            erreur = string.strip() == ''
        #  result
        return not erreur

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:
u=Utils()
print(u.is_string_ok("abcd")

wir schreiben

print(Utils.is_string_ok("abcd")

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:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils

print(Utils.is_string_ok("  "))
print(Utils.is_string_ok(47))
print(Utils.is_string_ok(" q "))
  • Zeilen 1–4: Wir verwenden ein Konfigurationsskript;

Das Konfigurationsskript [config.py] lautet wie folgt:

def configure():
    import os

    #  configuration file folder
    script_dir = os.path.dirname(os.path.abspath(__file__))

    #  absolute paths of folders to put in the syspath
    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"
    ]

    #  update syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    #  return the config
    return {}
  • Zeile 9: Der Ordner [shared] wird dem Python-Pfad hinzugefügt;

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]: Ü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;
#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils


#  a proprietary exception class derived from [BaseException]
class MyException(BaseException):
    #  do nothing: empty classroom
    pass


#  person class
class Personne:
    #  manufacturer
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        #  parameter storage
        #  initializations will be made via setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

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

    #  getters
    @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

    #  setters
    @prénom.setter
    def prénom(self, prénom: str):
        #  first name must be non-empty
        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):
        #  first name must be non-empty
        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):
        #  age must be an integer >=0
        erreur = False
        if isinstance(âge, int):
            if âge >= 0:
                self.__âge = âge
            else:
                erreur = True
        else:
            erreur = True
        #  mistake?
        if erreur:
            raise MyException("L'âge doit être un entier >=0")


#  ---------------------------------- main
#  a Person object
try:
    #  instantiation Person class
    p = Personne("Paul", "de la Hûche", 48)
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error display
    print(erreur)

#  another object Nobody
try:
    #  instantiation Person class
    p = Personne("xx", "yy", "zz")
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error display
    print(erreur)

#  another person without parameters this time
try:
    #  instantiation Person class
    p = Personne()
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  you cannot access the __attr private attributes of the
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}")

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:
p=Personne(…)
p.prénom=valeur

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:
p=Personne(…)
p.__prénom=valeur

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:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils


#  a proprietary exception class derived from [BaseException]
class MyException(BaseException):
    #  do nothing: empty classroom
    pass


#  person class
class Personne:
    #  manufacturer
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        #  parameter storage
        #  initializations will be made via setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  other initialization method
    def init_with_personne(self: object, p: object):
        #  initializes the current object with a person p
        self.__init__(p.prénom, p.nom, p.âge)

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

    #  getters
    

    #  setters
    


#  ---------------------------------- main
#  a Person object
try:
    #  instantiation Person class
    p = Personne("Paul", "de la Hûche", 48)
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another object Nobody
try:
    #  instantiation Person class
    p = Personne("xx", "yy", "zz")
    #  object display p
    print(f"p={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another person without parameters this time
try:
    #  instantiation Person class
    p = Personne()
    #  object display p
    print(f"p={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another Person obtained by recopying
try:
    #  instantiation Person class
    p2 = Personne()
    p2.init_with_personne(p)
    #  object display p2
    print(f"p2={p2}")
except MyException as erreur:
    #  error msg display
    print(erreur)

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:

Image

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

Das Skript [classes_07] zeigt, dass wir eine Liste von Objekten haben können:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne

#  ---------------------------------- main
#  create a list of person objects
groupe = [Personne("Paul", "Langevin", 48), Personne("Sylvie", "Lefur", 70)]
#  identity of these persons
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

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:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne, Enseignant

#  ---------------------------------- main
#  creation of an array of Personne and derived objects
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70)]
#  identity of these persons
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

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:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne, Enseignant, Etudiant

#  ---------------------------------- main
#  creation of an array of Personne and derived objects
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70),
          Etudiant("Steve", "Boer", 22, "iup2 qualité")]
#  identity of these persons
for personne in groupe:
    #  person display
    print(personne)

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:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Etudiant

#  ---------------------------------- main
#  student creation
étudiant=Etudiant("Steve", "Boer", 22, "iup2 qualité")
#  property dictionary
print(étudiant.__dict__)

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:

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 dem Objekt und dem Wörterbuch zu schlagen;