Metaklassen
Definition Metaklasse

Metaklassen werden nicht von allen objektorientierten Programmiersprachen unterstützt. Die Programmiersprachen, die sie implementiert haben, unterscheiden sich jedoch erheblich in der Art, wie die Metaklassen implementiert sind. Python unterstützt Metaklassen.
Manche Programmierer sehen Metaklassen in Python als "Lösungen, die auf eine Problem warten".
Dennoch gibt es zahlreiche mögliche Anwendungsgebiete für Metaklassen. Um nur einige zu nennen:
- logging und profiling
- Konsistenzprüfungen von Schnittstellen
- Registrierung von Klassen nach ihrer Entstehungszeit
- Automatisches Hinzufügen von Methoden
- Automatische Erzeugung von Properties
- Proxies
- Automatisches Locking und Synchronisation
Metaklassen definieren
Prinzipiell werden Metaklassen genau so definiert, wie jede andere Klasse in Python. Jedoch erben Metaklassen von "type". Ein weiterer Unterschied ist, dass eine Metaklasse automatisch aufgerufen wird, wenn das Klassen-Statement eine Metaklassen-Endung bekommt. Mit anderen Worten: Wenn kein "metaclass" Schlüsselwort im Klassen-Header benutzt wird, so wird type() aufgerufen. Wenn "metaclass" im Klassen-Header steht, wird die zugewiesene Klasse aufgerufen, statt type().Wir erstellen eine einfache Metaklasse. Sie ist nutzlos, ausser dass sie den Inhalt der Argumente in der __new__ Methode ausgibt und die Ergebnisse von type.__new__ zurückzugeben:
class LittleMeta(type): def __new__(cls, clsname, superclasses, attributedict): print("clsname: ", clsname) print("superclasses: ", superclasses) print("attributedict: ", attributedict) return type.__new__(cls, clsname, superclasses, attributedict)Im folgenden Beispiel benutzen wir die Metaklasse "LittleMeta":
class S: pass class A(S, metaclass=LittleMeta): pass a = A()Die Ausgabe bestätigt das bisher Geschriebene:
clsname: A superclasses: (<class '__main__.S'>,) attributedict: {'__module__': '__main__', '__qualname__': 'A'}Wir sehen, dass LittleMeta.__new__ aufgerufen wird und nicht type.__new__.
Kommen wir auf das letzt Kapitel zurück: Wir definieren eine Metaklasse "EssentialAnswers" die fähig ist, die Methode augment_answer automatisch anzufügen:
x = input("Do you need the answer? (y/n): ") if x: required = True else: required = False def the_answer(self, *args): return 42 class EssentialAnswers(type): def __init__(cls, clsname, superclasses, attributedict): if required: cls.the_answer = the_answer class Philosopher1(metaclass=EssentialAnswers): pass class Philosopher2(metaclass=EssentialAnswers): pass class Philosopher3(metaclass=EssentialAnswers): pass plato = Philosopher1() print(plato.the_answer()) kant = Philosopher2() # let's see what Kant has to say :-) print(kant.the_answer()) Do you need the answer? (y/n): y 42 42Wir haben im Kapitel "Beziehungen zwischen Klasse und Typ" gelernt, dass Python nach der Klassen-Definition folgenden Aufruf macht:
type(classname, superclasses, attributes_dict)Das passiert nicht, wenn eine Metaklasse im Header definiert wurde. Das haben wir im vorigen Beispiel gemacht. Unsere Klassen Philosopher1, Philosopher2 und Philosopher3 wurden mit der Metaklasse EssentialAnswers gekoppelt. Darum wird EssentialAnswer ausgeführt statt type.
EssentialAnswer(classname, superclasses, attributes_dict)Genauer gesagt werden die Argumente des Aufrufs wie folgt gesetzt:
EssentialAnswer('Philopsopher1', (), {'__module__': '__main__', '__qualname__': 'Philosopher1'})Analog dazu natürlich auch die anderen Philosopher-Klassen.
Singletons mit Metaklassen erstellen
Das Singleton-Muster ist ein Entwurfs-Muster welches die Erstellung von Instanzen auf eine beschränkt. Es wird dann genutzt, wenn nur ein Objekt gebraucht wird. Das Konzept kann, etwas verallgemeinert, auf die Instanziierung auf eine bestimmte Anzahl von Objekten begrenzt werden. In der Mathematik werden Singletons benutzt für Mengen mit exakt einem Element.class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class SingletonClass(metaclass=Singleton): pass class RegularClass(): pass x = SingletonClass() y = SingletonClass() print(x == y) x = RegularClass() y = RegularClass() print(x == y)
True False