Klassen und die dynamische Erzeugung von Klassen
Beziehung zwischen "class" und "type"

Zunächst konzentrieren wir uns auf die Verbindung zwischen "type" und "class". Als Sie eine Klasse definiert haben, haben Sie sich vielleicht gefragt, was hinter den "Zeilen" passiert. Wir haben bereits gesehen, dass type(object) die Klasse der Instanz zurückliefert:
x = [4, 5, 9] y = "Hello" print(type(x), type(y))Wir erhalten folgende Ausgabe:
<class 'list'> <class 'str'>Wenn type() auf die Namen der Klassen selbst angewendet werden, so erhalten wir "type" als Ergebnis.
print(type(list), type(str)) Output: <class 'type'> <class 'type'>Das gleiche Ergebnis erhalten wir mit folgendem Code:
x = [4, 5, 9] y = "Hello" print(type(x), type(y)) print(type(type(x)), type(type(y))) Output: <class 'list'> <class 'str'> <class 'type'> <class 'type'>Dies zeigt uns, dass Klassen wie 'list' und 'int' Instanzen der Klasse 'type' sind. Allgemein kann man sagen, dass alle bisher von uns selbst erstellten Klassen Instanzen der Klasse "type" sind.
In Python3 gibt es keine Unterschied "Klassen" und "Typen". In den meisten Fällen werden sie synonym verwendet.
Die Tatsache, dass Klassen Instanzen der Klasse "type" sind, erlaubt uns Meta-Klassen zu programmieren. Wir können Klassen kreieren, die von der Klasse "type" erben. Somit ist eine Meta-Klasse eine Subklasse der Klasse "type".
Statt eines einzigen Arguments, kann type mit drei Parametern aufgerufen werden:
type(classname, superclasses, attributes_dict)Wenn type mit drei Argumenten aufgerufen wird, so liefert es ein neues type-Objekt zurück. Das bietet uns eine dynamische Form des Klassen-Statements.
- "classname" ist der String, der den Klassen-Namen angibt und zum name-Attribut wird
- "superclasses" ist eine Liste oder ein Tupel mit den Ober-Klassen unserer Klassen. Die Liste oder das Tupel wird zum bases-Attribut.
- "attributes_dict" ist ein Dictionary, welches als Namensraum unserer Klasse fungiert. Es beinhaltet die Definitionen des Klassen-Körpers und wird zum dict-Attribut.
class A: pass x = A() print(type(x))Wir erhalten folgende Ausgabe:
<class '__main__.A'>Wir können type() ebenfalls für die oben beschriebene Klassen-Definition verwenden:
A = type("A", (), {}) x = A() print(type(x))Wir erhalten ebenfalls folgende Ausgabe:
<class '__main__.A'>Generell bedeutet das, dass wir Klassen mit
type(classname, superclasses, attributedict)definieren können.
Wenn wir type() aufrufen, wird die call-Methode von type aufgerufen. Die call-Methode ruft zwei weitere Methoden auf: new und init.
type.__new__(typeclass, classname, superclasses, attributedict) type.__init__(cls, classname, superclasses, attributedict)Die new-Methode erstellt und liefert das neue Klassen-Objekt zurück. Anschließend initialisiert die init-Methode das erstellte Objekt.
class Robot: counter = 0 def __init__(self, name): self.name = name def sayHello(self): return "Hi, I am " + self.name def Rob_init(self, name): self.name = name Robot2 = type("Robot2", (), {"counter":0, "__init__": Rob_init, "sayHello": lambda self: "Hi, I am " + self.name}) x = Robot2("Marvin") print(x.name) print(x.sayHello()) y = Robot("Marvin") print(y.name) print(y.sayHello()) print(x.__dict__) print(y.__dict__)Nach Ausführung des Codes erhalten wir folgende Ausgabe:
Marvin Hi, I am Marvin Marvin Hi, I am Marvin {'name': 'Marvin'} {'name': 'Marvin'}Die Klassen-Definitionen von Robot und Robot2 sind syntaktisch völlig verschieden. Aber logisch gesehen, implementieren beide genau das Gleiche.
Was Python im ersten Beispiel macht, ist, dass alle Methoden und Attribute der Klasse Robot gesammelt werden um sie dann als Parameter dem Argument attributes_dict beim type-Aufruf zu übergeben. Python macht also das gleiche, wie wir es mit der Klasse Robot2 gemacht haben.