13. Die generischen Klassen [BaseEntity] und [MyException]
Wir definieren nun zwei Klassen, die wir im weiteren Verlauf regelmäßig verwenden werden.

13.1. Die Klasse MyException
Die Klasse [MyException] (MyException.py) stellt eine proprietäre Ausnahmeklasse bereit:
# Eine benutzerdefinierte Ausnahmeklasse, die von [BaseException] abgeleitet ist
class MyException(BaseException):
# Konstruktor
def __init__(self: object, code: int, message: str):
# übergeordnete Klasse
BaseException.__init__(self, message)
# Fehlercode
self.code = code
# toString
def __str__(self):
return f"MyException[{self.code}, {super().__str__()}]"
# Getter
@property
def code(self) -> int:
return self.__code
# Setter
@code.setter
def code(self, code: int):
# Der Fehlercode muss eine positive ganze Zahl sein
if isinstance(code, int) and code > 0:
self.__code = code
else:
# Ausnahme
raise BaseException(f"code erreur {code} incorrect")
Hinweise
- Zeile 2: Die Klasse [MyException] leitet sich von der vordefinierten Klasse [BaseException] ab;
- Zeile 4: Der Konstruktor akzeptiert zwei Parameter:
- [code]: einen ganzzahligen Fehlercode;
- [message]: eine Fehlermeldung;
- Zeile 6: Die Fehlermeldung wird an die übergeordnete Klasse übergeben;
- Zeilen 14–27: Das Attribut [code] wird über einen Getter/Setter bearbeitet;
- Zeilen 23–24: Die Gültigkeit des Attributs [code] wird überprüft: Es muss sich um eine ganze Zahl > 0 handeln;
13.2. Die Klasse [BaseEntity]
Die Klasse [BaseEntity] wird die übergeordnete Klasse für die meisten Klassen sein, die wir erstellen werden, um Informationen über ein Objekt zu kapseln. Im weiteren Verlauf werden wir hauptsächlich zwei Arten von Klassen verwenden:
- Klassen, deren einziger Zweck darin besteht, Informationen über ein und dasselbe Objekt an einem Ort zu kapseln. Diese verfügen über keine anderen Verhaltensweisen (Methoden) als Getter/Setter und eine Anzeigefunktion (__str__). Wenn N Objekte zu verwalten sind, werden diese Klassen N-mal instanziiert. [BaseEntity] ist die übergeordnete Klasse dieser Art von Klassen;
- Klassen, deren Hauptaufgabe darin besteht, Methoden und sehr wenige Informationen zu kapseln. Diese Klassen werden nur einmal instanziiert (Singleton). Ihre Aufgabe ist es, die Algorithmen einer Anwendung zu implementieren;
Die Klasse [BaseEntity] sieht wie folgt aus:
# Importe
import json
import re
from MyException import MyException
class BaseEntity(object):
# aus dem Klassenstatus ausgeschlossene Eigenschaften
excluded_keys = []
# Eigenschaften der Klasse
@staticmethod
def get_allowed_keys() -> list:
# ID: Objekt-ID
return ["id"]
# toString
def __str__(self) -> str:
return self.asjson()
# Getter
@property
def id(self) -> int:
return self.__id
# Setter
@id.setter
def id(self, id):
# Die ID muss eine ganze Zahl >= 0 sein
try:
id = int(id)
erreur = id < 0
except:
erreur = True
# Fehler?
if erreur:
raise MyException(1, f"L'identifiant d'une entité {self.__class__} doit être un entier >=0")
else:
self.__id = id
def fromdict(self, state: dict, silent=False):
…
def set_value(self, key: str, value, new_attributes) -> dict:
…
def asdict(self, included_keys: list = None, excluded_keys: list = []) -> dict:
…
def asjson(self, excluded_keys: list = []) -> str:
…
def fromjson(self, json_state: str):
…
Kommentare
- Das Ziel der Klasse [BaseEntity] ist es, die Konvertierungen „Objekt / Wörterbuch“ und „Objekt / jSON“ zu vereinfachen. Dazu werden folgende Methoden bereitgestellt:
- [asdict]: Gibt das Eigenschaftswörterbuch des Objekts zurück;
- [fromdict]: Erstellt ein Objekt aus einem Wörterbuch;
- [asjson]: Gibt die Zeichenkette jSON des Objekts zurück, wie es die Funktion [__str__] tut;
- [fromjson]: Erstellt ein Objekt anhand seiner Zeichenkette jSON;
- Die Klasse [BaseEntity] ist dazu bestimmt, abgeleitet zu werden, und nicht dazu, unverändert verwendet zu werden;
- Zeilen 22–25: Die Klasse [BaseEntity] hat nur eine Eigenschaft, die Ganzzahl [id]. Diese Eigenschaft ist die Kennung des Objekts. In der Praxis ist es oft nützlich, Instanzen derselben Klasse voneinander unterscheiden zu können. Dies erreichen wir mit dieser Eigenschaft, die für jede Instanz eindeutig ist. Außerdem stammen Objekte oft aus Datenbanken, in denen sie durch einen Primärschlüssel identifiziert werden, in der Regel eine Ganzzahl. In diesen Fällen ist [id] der Primärschlüssel;
- Zeilen 27–40: Der Setter für die Eigenschaft [id]. Es wird überprüft, ob es sich um eine ganze Zahl >= 0 handelt. Ist dies nicht der Fall, wird eine Ausnahme vom Typ [MyException] ausgelöst (Zeile 39);
- Zeile 10: [excluded_keys] ist ein Klassenattribut und kein Instanzattribut. Daher wird [BaseEntity.excluded_keys] geschrieben. Dieses Klassenattribut ist eine Liste, die die Klassen-Eigenschaften enthält, die nicht an den Konvertierungen „Objekt / Wörterbuch“ und „Objekt / jSON“ beteiligt sind;
- Zeilen 12–16: [get_allowed_keys] gibt die Liste der Eigenschaften der Klasse zurück. Bei einer Konvertierung von „Dictionary“ nach „Objekt“ oder von jSON nach „Objekt“ werden nur die Schlüssel akzeptiert, die in dieser Liste enthalten sind. Jede Klasse, die von der Klasse [BaseEntity] abgeleitet ist, muss diese Liste neu definieren;
Man muss hier verstehen, dass die Eigenschaften und Funktionen der Klasse [BaseEntity] für die von [BaseEntity] abgeleiteten Klassen zugänglich sind. Das ist der wichtige Punkt, den es zu verstehen gilt.
Wir werden den Code der Klasse [BaseEntity] im Detail betrachten. Er ist recht fortgeschritten. Anfänger können sich darauf beschränken, die Funktion der einzelnen Methoden zu lesen, ohne sich mit dem Code selbst zu befassen.
13.2.1. Die Methode [BaseEntity.fromdict]
13.2.1.1. Définition
Die Methode [fromdict] ermöglicht es, ein Objekt vom Typ [BaseEntity] oder ein davon abgeleitetes Objekt anhand eines Dictionaries zu initialisieren:
def fromdict(self, state: dict, silent=False):
# Das Objekt wird aktualisiert
# Zulässige Schlüssel
allowed_keys = self.__class__.get_allowed_keys()
# Durchsucht Status-Schlüssel
for key, value in state.items():
# Ist der Schlüssel zulässig?
if key not in allowed_keys:
if not silent:
raise MyException(2, f"la clé {key} n'est pas autorisée")
else:
# Es wird versucht, den Wert dem Schlüssel zuzuweisen
# Eine eventuelle Ausnahme wird weitergeleitet
setattr(self, key, value)
# Das Objekt wird zurückgegeben
return self
Kommentare
- Zeile 1: Die Funktion erhält als Parameter das Wörterbuch [state], anhand dessen das aktuelle Objekt initialisiert wird;
- Zeile 4: Es wird die statische Funktion [get_allowed_keys] der Klasse aufgerufen, die die Funktion [fromdict] aufgerufen hat. Handelt es sich um eine von [BaseEntity] abgeleitete Klasse und hat diese abgeleitete Klasse die statische Funktion [get_allowed_keys] neu definiert, dann wird die Funktion [get_allowed_keys] aufgerufen. Jede abgeleitete Klasse definiert diese statische Funktion neu, um darin ihre Eigenschaften zu deklarieren;
- Zeile 6: Die Schlüssel und Werte des Wörterbuchs [state] werden durchlaufen;
- Zeile 8: Wenn der Schlüssel [key] nicht zu den Eigenschaften der Klasse gehört, dann gilt entweder:
- wird er ignoriert;
- es wird eine Ausnahme ausgelöst (Zeile 10). Der Entwickler gibt an, was er möchte, indem er den richtigen Parameter [silent] übergibt (Zeile 1). Der Standardwert von [silent] bewirkt, dass eine Ausnahme ausgelöst wird, wenn versucht wird, das Objekt mit einer Eigenschaft zu initialisieren, die es nicht besitzt;
- Zeile 14: Wenn der Schlüssel zu den Eigenschaften des Objekts gehört, wird er dem Objekt [self] mithilfe der vordefinierten Funktion [setattr] zugewiesen;
- Zeile 16: Die Funktion gibt das initialisierte Objekt zurück;
13.2.1.2. Exemples

13.2.1.2.1. Die Klasse [Utils]
Die Klasse [Utils] (Utils.py) lautet wie folgt:
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
Sie definiert in den Zeilen 3–11 eine statische Methode, die einen booleschen Wert „true“ zurückgibt, wenn ihr Parameter [str] eine nicht leere Zeichenkette ist;
13.2.1.2.2. Die Klasse [Personne]
Die Klasse [Personne] (Personne.py) leitet sich von der Klasse [BaseEntity] ab:
# Importe
from BaseEntity import BaseEntity
from MyException import MyException
from Utils import Utils
# Klasse „Person“
class Personne(BaseEntity):
# aus dem Zustand der Klasse ausgeschlossene Eigenschaften
excluded_keys = []
# Eigenschaften der Klasse
# ID: Identifikationsnummer der Person
# Vorname: Vorname der Person
# Nachname: Nachname der Person
# Alter: Alter der Person
@staticmethod
def get_allowed_keys() -> list:
# ID: Kennung des Objekts
return BaseEntity.get_allowed_keys() + ["nom", "prénom", "â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(11, "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(12, "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(13, "L'âge doit être un entier >=0")
- Zeile 8: Die Klasse [Personne] leitet sich von der Klasse [BaseEntity] ab;
- Zeilen 8–65: Der wesentliche Teil der bereits behandelten Klasse [Personne] wurde beibehalten. Die Unterschiede sind folgende:
- Die Klasse hat keinen Konstruktor mehr;
- die Klasse verwendet die Ausnahme [MyException], Beispiel in Zeile 65;
- sie verfügt über eine statische Methode, [get_allowed_keys] (Zeilen 17–20), die die Liste ihrer Eigenschaften definiert. Die klassen-spezifischen Eigenschaften von [Personne] werden zu denen der übergeordneten Klasse [BaseEntity] hinzugefügt;
- sie verfügt über eine statische Liste [excluded_keys], auf die wir noch zurückkommen werden;
13.2.1.2.3. Die Klasse [Enseignant]
Die Klasse [Enseignant] (Enseignant.py) leitet sich von der Klasse [Personne] ab:
# Importe
from MyException import MyException
from Personne import Personne
from Utils import Utils
# Klasse „Lehrer“
class Enseignant(Personne):
# aus dem Klassenstatus ausgeschlossene Eigenschaften
excluded_keys = []
# Eigenschaften der Klasse
# ID: Identifikationsnummer der Person
# Vorname: Vorname der Person
# Nachname: Nachname der Person
# Alter: Alter der Person
# Fach: unterrichtetes Fach
@staticmethod
def get_allowed_keys() -> list:
# ID: Objekt-ID
return Personne.get_allowed_keys() + ["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(21, "La discipline doit être une chaîne de caractères non vide")
# Methode „show“
def show(self):
print(f"Enseignant[{self.id}, {self.prénom}, {self.nom}, {self.âge}]")
- Zeile 8: Die Klasse [Enseignant] erweitert (oder leitet sich ab von) der Klasse [Personne];
- Zeilen 18–21: definieren die Liste der Eigenschaften der Klasse;
- Zeilen 37–38: Die Methode [show] zeigt die Identität des Lehrers an;
13.2.1.2.4. Die Konfiguration [config]
Die Beispielskripte verwenden die folgende Konfiguration [config]:
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 = [
# die Klasse BaseEntity
f"{script_dir}/entities",
]
# Aktualisierung des Syspath
from myutils import set_syspath
set_syspath(absolute_dependencies)
# Die Konfiguration wird angepasst
return {}
- Zeilen 8–10: Die Ordner, die die Projektabhängigkeiten enthalten;
- Zeilen 14–15: Der Python-Pfad wird aufgebaut;
- Zeile 18: Es wird ein leeres Wörterbuch zurückgegeben (es gibt keine andere Konfiguration als die des syspath);
13.2.1.2.5. Das Skript [fromdict_01]
Das Skript [fromdict_01] lautet wie folgt:
# die Anwendung wird konfiguriert
import config
config = config.configure()
# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from Enseignant import Enseignant
# ein Lehrer
enseignant1 = Enseignant().fromdict({"id": 1, "nom": "lourou", "prénom": "paul", "âge": 56})
enseignant1.show()
- Zeile 10: Es wird ein Objekt vom Typ [Enseignant] anhand eines Dictionaries erstellt. Dazu wird der Standardkonstruktor der Klasse verwendet, um ein Objekt vom Typ [Enseignant] zu erstellen, auf das anschließend die Methode [fromdict] angewendet wird. Man muss verstehen, dass hier die Methode [fromdict] ausgeführt wird, die zur übergeordneten Klasse [BaseEntity] gehört. Denn:
- Die Methode [fromdict] wird zunächst in der Klasse [Enseignant] gesucht. Sie existiert nicht;
- anschließend wird in der übergeordneten Klasse [Personne] danach gesucht. Sie existiert nicht;
- anschließend wird in der übergeordneten Klasse [BaseEntity] danach gesucht. Diese existiert;
- Zeile 11: Das Objekt [Enseignant] wird angezeigt;
Die Ergebnisse lauten wie folgt:
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/02/fromdict_01.py
Enseignant[1, paul, lourou, 56]
Process finished with exit code 0
13.2.1.2.6. Das Skript [fromdict_02]
Das Skript [fromdict_02] lautet wie folgt:
# Die Anwendung wird konfiguriert
import config
config = config.configure()
# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from Enseignant import Enseignant
# ein Lehrer
enseignant1 = Enseignant().fromdict({"id": 1, "nom": "lourou", "prénom": "", "âge": 56})
enseignant1.show()
- Zeile 10: Es wird ein Lehrer mit einem leeren Vornamen angelegt. Dies sollte eine Ausnahme auslösen, da die Klasse [Personne] keine leeren Vornamen akzeptiert. Dieses Beispiel verdeutlicht den Unterschied zwischen einem Dictionary und einem Objekt. Letzteres kann die Gültigkeit seiner Eigenschaften überprüfen, das Dictionary hingegen nicht;
Die Ergebnisse lauten wie folgt:
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/02/fromdict_02.py
Traceback (most recent call last):
File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/02/fromdict_02.py", line 10, in <module>
enseignant1 = Enseignant().fromdict({"id": 1, "nom": "lourou", "prénom": "", "âge": 56})
File "C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\classes\02/entities\BaseEntity.py", line 55, in fromdict
setattr(self, key, value)
File "C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\classes\02/entities\Personne.py", line 42, in prénom
raise MyException(11, "Le prénom doit être une chaîne de caractères non vide")
MyException.MyException: MyException[11, Le prénom doit être une chaîne de caractères non vide]
Process finished with exit code 1
13.2.1.2.7. Das Skript [fromdict_03]
Das Skript [fromdict_03] lautet wie folgt:
# Die Anwendung wird konfiguriert
import config
config = config.configure()
# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from Enseignant import Enseignant
# ein Lehrer
enseignant1 = Enseignant().fromdict({"id": 1, "nom": "lourou", "prénom": "albert", "âge": 56, "sexe": "M"})
enseignant1.show()
- Zeile 10: Es wird ein Lehrer anhand eines Wörterbuchs erstellt, das einen Schlüssel (Geschlecht) enthält, der nicht zur Klasse [Enseignant] gehört. In diesem Fall sollte eine Ausnahme ausgelöst werden;
Die Ergebnisse lauten wie folgt:
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/02/fromdict_03.py
Traceback (most recent call last):
File "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/02/fromdict_03.py", line 10, in <module>
enseignant1 = Enseignant().fromdict({"id": 1, "nom": "lourou", "prénom": "albert", "âge": 56, "sexe": "M"})
File "C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\classes\02/entities\BaseEntity.py", line 51, in fromdict
raise MyException(2, f"la clé [{key}] n'est pas autorisée")
MyException.MyException: MyException[2, la clé [sexe] n'est pas autorisée]
Process finished with exit code 1
13.2.1.2.8. Das Skript [fromdict_04]
Das Skript [fromdict_04] ist eine Kopie von [fromdict_03], bis auf ein Detail:
# Die Anwendung wird konfiguriert
import config
config = config.configure()
# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from Enseignant import Enseignant
# ein Lehrer
enseignant1 = Enseignant().fromdict({"id": 1, "nom": "lourou", "prénom": "albert", "âge": 56, "sexe": "M"}, silent=True)
enseignant1.show()
- Zeile 10: Der Parameter [silent=True] wurde verwendet, um anzugeben, dass ein Schlüssel aus dem Wörterbuch, der keine Eigenschaft der Klasse [Enseignant] ist, einfach ignoriert werden soll. In diesem Fall wird keine Ausnahme ausgelöst;
Die Ergebnisse lauten wie folgt:
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/02/fromdict_04.py
Enseignant[1, albert, lourou, 56]
Process finished with exit code 0
13.2.2. Die Methode [BaseEntity.asdict]
13.2.2.1. Définition
Die Methode [BaseEntity.asdict] gibt ein Wörterbuch zurück, dessen Schlüssel die Eigenschaften des Objekts sind:
def asdict(self, included_keys: list = None, excluded_keys: list =[]) -> dict:
# Objektattribute
attributes = self.__dict__
# die neuen Attribute
new_attributes = {}
# Die Attribute werden durchlaufen
for key, value in attributes.items():
# wenn der Schlüssel explizit angefordert wird
if included_keys and key in included_keys:
self.set_value(key, value, new_attributes)
# ansonsten, wenn der Schlüssel nicht ausgeschlossen ist
elif not included_keys and key not in self.__class__.excluded_keys and key not in excluded_keys:
self.set_value(key, value, new_attributes)
# das Attributwörterbuch wird zurückgegeben
return new_attributes
Anmerkungen
- Zeile 1: Die Funktion [asdict] gibt das Wörterbuch der Eigenschaften des Objekts zurück;
- Zeile 1: [included_keys]: die Liste der Schlüssel, die in das Wörterbuch aufgenommen werden sollen;
- Zeile 1: [excluded_keys]: die Liste der Schlüssel, die aus dem Wörterbuch ausgeschlossen werden sollen;
- Zeile 3: Die Eigenschaft [self.__dict__] gibt das Eigenschaftsverzeichnis des Objekts zurück. Die Namen der Eigenschaften sind die Schlüssel und ihre Werte die Werte des Wörterbuchs. Ein Objekt kann Verweise auf andere Objekte enthalten. Den Namen der Eigenschaften wird dann der Name der Klasse vorangestellt, zu der sie gehören. Das ist nicht erwünscht. Wir wollen die Eigenschaften ohne ihr Präfix;
- Zeile 3: Hier ist zu beachten, dass, wenn die Funktion [asdict] innerhalb einer von [BaseEntity] abgeleiteten Klasse ausgeführt wird, die Eigenschaft [self.__dict__] das Eigenschaftswörterbuch des abgeleiteten Objekts zurückgibt;
- Zeile 5: das Wörterbuch, das erstellt werden soll;
- Zeile 7: Die Werte von [self.__dict__] werden in der Form (Schlüssel, Wert) durchlaufen;
- Zeile 9: Wenn der aktuelle Schlüssel in der Liste der einzufügenden Schlüssel enthalten ist, wird er mithilfe der Funktion [set_value], die wir im Folgenden beschreiben werden, zum Wörterbuch [new_attributes] hinzugefügt;
- Zeile 12: Wenn der Parameter [included_keys] nicht vorhanden ist, wird der Parameter [excluded_keys] verwendet. Wenn die Eigenschaft nicht zu den auszuschließenden Eigenschaften gehört, wird sie dem Wörterbuch [new_attributes] hinzugefügt;
- Zeile 12: Es gibt mehrere Möglichkeiten, eine Eigenschaft aus dem Wörterbuch auszuschließen:
- Sie wurde auf der Ebene des Klassenattributs [excluded_keys] definiert;
- sie wurde in der Liste [excluded_keys] definiert, die an die Funktion [asdict] übergeben wurde;
- der Parameter [included_keys] ist vorhanden und enthält die Eigenschaft nicht;
- Zeile 15: Das Wörterbuch [new_attributes] wird zurückgegeben
Die Funktion [set_value] aus den Zeilen 10 und 13 lautet wie folgt:
@staticmethod
def set_value(key: str, value, new_attributes: dict):
# Die Schlüssel können die Form __Class__key haben
match = re.match("^.*?__(.*?)$", key)
if match:
# man notiert den neuen Schlüssel
newkey = match.groups()[0]
else:
# Der Schlüssel bleibt unverändert
newkey = key
# Der neue Schlüssel wird in das Wörterbuch eingefügt: [new_attributes]
# wobei der zugehörige Wert bei Bedarf in einen der folgenden Typen umgewandelt wird
# „dict“, „list“ oder „einfacher Typ“
new_attributes[newkey] = BaseEntity.check_value(value)
Kommentare
- Zeile 4: Es wird geprüft, ob der Schlüssel die Form __Class_key hat. Diese Form hat er, wenn er zu einem Objekt gehört, das im Hauptobjekt enthalten ist. In diesem Fall soll nur die Zeichenkette [key] beibehalten werden;
- Zeile 7: Es wird nur die Zeichenfolge beibehalten, die auf die beiden letzten unterstrichenen Zeichen der Zeichenfolge folgt;
- Zeile 8–10: Wenn der Schlüssel nicht die Form __Class_key hat, wird er unverändert beibehalten;
- Zeilen 11–14: Der dem Schlüssel „[newkey]“ zugeordnete Wert wird durch die statische Methode „[BaseEntity.check_value]“ berechnet;
Die statische Methode [BaseEntity.check_value] lautet wie folgt:
@staticmethod
def check_value(value):
# Der Wert kann vom Typ BaseEntity, „list“, „dict“ oder einem einfachen Typ sein
# Ist der Wert eine Instanz von BaseEntity?
if isinstance(value, BaseEntity):
value2 = value.asdict()
# Ist „value“ vom Typ „list“?
elif isinstance(value, list):
value2 = BaseEntity.list2list(value)
# Ist „value“ vom Typ „dict“?
elif isinstance(value, dict):
value2 = BaseEntity.dict2dict(value)
# value ist ein einfacher Typ
else:
value2 = value
# Das Ergebnis wird zurückgegeben
return value2
- Zeile 1: Die Methode [check_value] ist statisch (Klassenmethode, keine Instanzmethode). Sie erhält als Parameter den Wert, der einem Schlüssel des Wörterbuchs zugeordnet werden soll:
- Zeile 17: Ist dieser Wert ein einfacher Typ, bleibt er unverändert;
- Zeilen 5–6: Ist dieser Wert vom Typ BaseEntity, wird er durch sein Wörterbuch ersetzt. Es handelt sich dann um einen rekursiven Aufruf;
- Zeilen 8–9: Ist dieser Wert eine Liste, wird er durch den Wert [BaseEntity.list2list] ersetzt;
- Zeilen 11–12: Ist dieser Wert ein Dictionary, wird er durch den Wert [BaseEntity.dict2dict] ersetzt;
Die statische Methode [BaseEntity.list2list] lautet wie folgt:
@staticmethod
def list2list(liste: list) -> list:
# Die Elemente der Liste werden überprüft
newlist = []
for value in liste:
newlist.append(BaseEntity.check_value(value))
# Die neue Liste wird zurückgegeben
return newlist
- Zeile 2: Die Methode erhält eine Liste und gibt eine Liste zurück;
- Zeilen 5–6: Jeder Wert der als Parameter übergebenen Liste wird durch den Wert ersetzt, der von der statischen Methode [BaseEntity.check_value] zurückgegeben wird. Es handelt sich also um einen rekursiven Aufruf. Die statische Methode [BaseEntity.check_value] wird so lange aufgerufen, bis ihr Parameter [value] ein einfacher Typ ist (kein Typ BaseEntity, keine Liste und kein Dict);
Die statische Methode [BaseEntity.dict2dict] lautet wie folgt:
@staticmethod
def dict2dict(dictionary: dict) -> dict:
# die Elemente des Wörterbuchs werden überprüft
newdict = {}
for key, value in dictionary.items():
newdict[key] = BaseEntity.check_value(value)
# das neue Wörterbuch wird zurückgegeben
return newdict
- Zeile 2: Die Methode erhält ein Diktionär und gibt ein Diktionär zurück;
- Zeilen 5–6: Jeder Wert des als Parameter übergebenen Dictionaries wird durch den Wert ersetzt, der von der statischen Methode [BaseEntity.check_value] zurückgegeben wird. Es handelt sich also um einen rekursiven Aufruf. Die statische Methode [BaseEntity.check_value] wird so lange aufgerufen, bis ihr Parameter [value] ein einfacher Typ ist (kein Typ BaseEntity, keine Liste und kein Dict);
13.2.2.2. Exemples
Das Skript [asdict_01] zeigt verschiedene Anwendungsmöglichkeiten der Methode [asdict]:
# die Anwendung wird konfiguriert
import config
config = config.configure()
# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from Enseignant import Enseignant
from BaseEntity import BaseEntity
# ein Lehrer
enseignant1 = Enseignant().fromdict({"id": 1, "nom": "lourou", "prénom": "paul", "âge": 56})
dict1 = enseignant1.asdict()
print(type(dict1))
print(enseignant1.__dict__)
print(dict1)
print(enseignant1.asdict(excluded_keys=["_Personne__âge"]))
Enseignant.excluded_keys = ["_Personne__prénom"]
print(enseignant1)
# ein weiterer Lehrer
enseignant2 = Enseignant().fromdict({"id": 2, "nom": "abélard", "prénom": "béatrice", "âge": 57})
print(enseignant2.asdict())
print(enseignant2.asdict(included_keys=["_Personne__nom"]))
# eine Liste von Entitäten innerhalb einer Entität
Enseignant.excluded_keys = []
entity1 = BaseEntity()
enseignants = [enseignant1, enseignant2]
setattr(entity1, "enseignants", enseignants)
print(entity1.asdict())
# ein Entitätswörterbuch innerhalb einer Entität
matières = {"maths": enseignant1, "français": enseignant2}
setattr(entity1, "matières", matières)
print(entity1.asdict())
Die Ergebnisse der Ausführung lauten wie folgt:
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/02/asdict_01.py
<class 'dict'>
{'_BaseEntity__id': 1, '_Personne__nom': 'lourou', '_Personne__prénom': 'paul', '_Personne__âge': 56}
{'id': 1, 'nom': 'lourou', 'prénom': 'paul', 'âge': 56}
{'id': 1, 'nom': 'lourou', 'prénom': 'paul'}
{"id": 1, "nom": "lourou", "âge": 56}
{'id': 2, 'nom': 'abélard', 'âge': 57}
{'nom': 'abélard'}
{'enseignants': [{'id': 1, 'nom': 'lourou', 'prénom': 'paul', 'âge': 56}, {'id': 2, 'nom': 'abélard', 'prénom': 'béatrice', 'âge': 57}]}
{'enseignants': [{'id': 1, 'nom': 'lourou', 'prénom': 'paul', 'âge': 56}, {'id': 2, 'nom': 'abélard', 'prénom': 'béatrice', 'âge': 57}], 'matières': {'maths': {'id': 1, 'nom': 'lourou', 'prénom': 'paul', 'âge': 56}, 'français': {'id': 2, 'nom': 'abélard', 'prénom': 'béatrice', 'âge': 57}}}
Process finished with exit code 0
- Zeile 4 verdeutlicht den Vorteil der Methode [asdict] gegenüber der Verwendung der Eigenschaft [__dict__]. Den Eigenschaften wird das Präfix ihrer Klasse entfernt. Dies eignet sich besser für die Anzeige;
- Es gibt mehrere Möglichkeiten, die Methode [asdict] zu verwenden:
- Man möchte alle Eigenschaften: Man verwendet die Methode [asdict] ohne Parameter;
- man möchte nur bestimmte Eigenschaften:
- Es gibt mehr Eigenschaften, die einbezogen werden sollen, als solche, die ausgeschlossen werden sollen: Es wird ausschließlich der Parameter [excluded_keys] verwendet;
- Es gibt weniger Eigenschaften, die einbezogen werden sollen als ausgeschlossen werden sollen: Es wird der einzige Parameter [included_keys] verwendet;
13.2.3. Die Methode [BaseEntity.asjson]
Mit dieser Methode lässt sich die Zeichenfolge jSON aus einem Objekt vom Typ [BaseEntity] oder einem davon abgeleiteten Objekt abrufen. Sie gibt die Zeichenfolge jSON aus dem durch die Methode [asdict] zurückgegebenen Wörterbuchs zurück. Der Code lautet wie folgt:
def asjson(self, included_keys: list = None, excluded_keys: list = []) -> str:
# die JSON-Zeichenkette
return json.dumps(self.asdict(included_keys=included_keys, excluded_keys=excluded_keys), ensure_ascii=False)
- Zeile 1: Die Parameter der Methode [asjson] entsprechen denen der Methode [asdict];
Hier ist ein Beispiel (asjson_01) für die Verwendung dieser Methode:
# Die Anwendung wird konfiguriert
import config
config = config.configure()
# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from Enseignant import Enseignant
from BaseEntity import BaseEntity
# ein Lehrer
enseignant1 = Enseignant().fromdict({"id": 1, "nom": "lourou", "prénom": "paul", "âge": 56})
print(type(enseignant1.asjson()))
print(enseignant1.asjson(excluded_keys=["_Personne__âge"]))
Enseignant.excluded_keys = ["_Personne__prénom"]
print(enseignant1.asjson())
# ein weiterer Lehrer
enseignant2 = Enseignant().fromdict({"id": 2, "nom": "abélard", "prénom": "béatrice", "âge": 57})
print(enseignant2.asjson())
print(enseignant2.asjson(included_keys=["_Personne__nom"]))
# eine Liste von Entitäten in einer Entität
Enseignant.excluded_keys = []
entity1 = BaseEntity()
enseignants = [enseignant1, enseignant2]
setattr(entity1, "enseignants", enseignants)
print(entity1.asjson())
# ein Entitätswörterbuch in einer Entität
matières = {"maths": enseignant1, "français": enseignant2}
setattr(entity1, "matières", matières)
print(entity1.asjson())
Die Ergebnisse lauten wie folgt:
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/02/asjson_01.py
<class 'str'>
{"id": 1, "nom": "lourou", "prénom": "paul"}
{"id": 1, "nom": "lourou", "âge": 56}
{"id": 2, "nom": "abélard", "âge": 57}
{"nom": "abélard"}
{"enseignants": [{"id": 1, "nom": "lourou", "prénom": "paul", "âge": 56}, {"id": 2, "nom": "abélard", "prénom": "béatrice", "âge": 57}]}
{"enseignants": [{"id": 1, "nom": "lourou", "prénom": "paul", "âge": 56}, {"id": 2, "nom": "abélard", "prénom": "béatrice", "âge": 57}], "matières": {"maths": {"id": 1, "nom": "lourou", "prénom": "paul", "âge": 56}, "français": {"id": 2, "nom": "abélard", "prénom": "béatrice", "âge": 57}}}
Process finished with exit code 0
Die Methode [BaseEntity.__str__] verwendet die Methode [asjson], um die Identität des Objekts [BaseEntity] oder eines davon abgeleiteten Objekts anzuzeigen:
# toString
def __str__(self) -> str:
return self.asjson()
13.2.4. Die Methode [BaseEntity.fromjson]
Die Methode [BaseEntity.fromjson] ermöglicht es, ein Objekt vom Typ [BaseEntity] oder ein davon abgeleitetes Objekt anhand eines Wörterbuchs vom Typ jSON zu initialisieren. Der Code lautet wie folgt:
def fromjson(self, json_state: str, silent: bool = False):
# Der Status des Objekts wird anhand der Zeichenfolge jSON aktualisiert
return self.fromdict(json.loads(json_state), silent=silent)
- Zeile 1: Die Methode akzeptiert zwei Parameter:
- [json_state]: das Wörterbuch jSON, das zur Initialisierung des Objekts [BaseEntity] verwendet wird;
- [silent]: um anzugeben, ob das Vorhandensein eines Schlüssels im Wörterbuch jSON, der nicht als Eigenschaft des Objekts [BaseEntity] akzeptiert werden kann, eine Ausnahme auslöst (silent=False) oder einfach ignoriert wird (silent=True);
- Zeile 3: Zunächst wird das Python-Wörterbuch als Abbild des Wörterbuchs jSON erstellt, anschließend wird das Objekt [BaseEntity] mithilfe der Methode [fromdict] anhand dieses Python-Wörterbuchs initialisiert;
Hier ein Beispiel (fromjson_01):
# Die Anwendung wird konfiguriert
import config
config = config.configure()
# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from Enseignant import Enseignant
import json
# ein Lehrer
json1 = json.dumps({"id": 1, "nom": "lourou", "prénom": "paul", "âge": 56})
enseignant1 = Enseignant().fromjson(json1)
enseignant1.show()
- Zeile 11: Die Zeichenkette jSON wird aus einem Wörterbuch erstellt;
- Zeile 12: Ein Objekt [Enseignant] wird mit dieser Zeichenkette initialisiert;
- Zeile 13: Der Lehrer wird angezeigt;
Die Ergebnisse lauten wie folgt:
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/02/fromjson_01.py
Enseignant[1, paul, lourou, 56]
Process finished with exit code 0
13.2.5. Das Skript [main]
Das Skript [main] fasst die verschiedenen behandelten Methoden zusammen:
# Die Anwendung wird konfiguriert
import config
config = config.configure()
# Der Syspath ist konfiguriert – die Importe können durchgeführt werden
from BaseEntity import BaseEntity
from MyException import MyException
# eine Klasse
class ChildEntity(BaseEntity):
# Attribute, die vom Klassenstatus ausgeschlossen sind
excluded_keys = []
@staticmethod
def get_allowed_keys():
return ["att1", "att2", "att3", "att4"]
@property
def att1(self) -> int:
return self.__att1
@att1.setter
def att1(self, value: int):
if 10 >= value >= 1:
self.__att1 = value
else:
raise MyException(1, f"L'attribut [att1] attend une valeur dans l'intervalle [1,10] ({value})")
# Konfiguration ChildEntity
ChildEntity.excluded_keys = []
# Instanz ChildEntity
child = ChildEntity().fromdict({"att1": 1, "att2": 2})
# Achtung bei den Namen der Eigenschaften
# Diese Namen werden in [excluded_keys] und [included_keys] verwendet
print(child.__dict__)
# Eigenschaften ohne Klassenpräfix
print(child)
# Instanz ChildEntity
try:
child = ChildEntity().fromdict({"att1": 1, "att5": 5})
print(child)
except MyException as erreur:
print(erreur)
# Instanz ChildEntity
child = ChildEntity().fromdict({"att1": 1, "att2": 2, "att3": 3, "att4": 4})
print(child)
# Ausschluss bestimmter Schlüssel aus dem Instanzstatus
ChildEntity.excluded_keys = ['att3']
print(child)
# Ein Schlüssel wird explizit aus der Anzeige ausgeschlossen
# Er wird zu den Schlüsseln hinzugefügt, die auf Klassenebene global ausgeschlossen sind
print(child.asdict(excluded_keys=["_ChildEntity__att1"]))
print(child.asjson(excluded_keys=["att2"]))
# Bedeutung der Klasse im Hinblick auf das Wörterbuch
# Sie kann die Gültigkeit ihres Inhalts überprüfen
try:
child = ChildEntity().fromdict({"att1": 20})
except MyException as erreur:
print(erreur)
# Instanz ChildEntity
child1 = ChildEntity().fromdict({"att1": 1, "att2": 2, "att3": 3, "att4": 4})
# Instanz ChildEntity, die eine weitere Instanz ChildEntity enthält
child2 = ChildEntity().fromdict({"att1": 10, "att2": 20, "att3": 30, "att4": child1})
print(child2)
# included_keys hat Vorrang vor excluded_keys, die daraufhin ignoriert werden
ChildEntity.excluded_keys = ['_ChildEntity__att1', 'att2']
print(child.asdict(included_keys=["_ChildEntity__att1", "att3"], excluded_keys=["att3", "att4"]))
Die Ergebnisse der Ausführung lauten wie folgt:
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/02/main.py
{'_ChildEntity__att1': 1, 'att2': 2}
{"att1": 1, "att2": 2}
MyException[2, la clé [att5] n'est pas autorisée]
{"att1": 1, "att2": 2, "att3": 3, "att4": 4}
{"att1": 1, "att2": 2, "att4": 4}
{'att2': 2, 'att4': 4}
{"att1": 1, "att4": 4}
MyException[1, L'attribut [att1] attend une valeur dans l'intervalle [1,10] (20)]
{"att1": 10, "att2": 20, "att4": {"att1": 1, "att2": 2, "att4": 4}}
{'att1': 1, 'att3': 3}
Process finished with exit code 0
Beachten Sie Zeile 2 der Ergebnisse: Es ist die Eigenschaft [ChildEntity.__dict__] (Zeile 38 des Codes), die es uns ermöglicht, die Namen der Eigenschaften zu ermitteln, die in die Listen [included_keys] und [excluded_keys] aufgenommen werden sollen. Es ist zu beachten – ebenfalls in Zeile 2 der Ergebnisse –, dass je nachdem, ob die Eigenschaft innerhalb der Klasse durch einen Getter/Setter definiert ist oder wie der Schlüssel eines Wörterbuchs angelegt wurde, ihr entweder der Name der Klasse [ChildEntity] vorangestellt wird oder nicht.