Benutzerdefinierte Metaklassen schreiben
Verwenden Sie __new__ und __init__ in Metaklassen, um die Klassenerstellung anzupassen.
Benutzerdefinierte Metaklassen schreiben ist eine kostenlose Python Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Python Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Python Academy-Kurs umfasst insgesamt 4 Lektionen.
Eine Metaklasse deklarieren
Geben Sie metaclass=MyMeta in der Klassendefinition an, um eine benutzerdefinierte Metaklasse zu verwenden. Die Metaklasse muss von type erben.
class MyMeta(type):
pass
class Foo(metaclass=MyMeta):
pass
print(type(Foo)) # <class '__main__.MyMeta'>__new__ überschreiben
__new__(mcs, name, bases, namespace) erstellt das Klassenobjekt. Verwenden Sie die Methode, um den Namespace zu ändern, bevor die Klasse aufgebaut wird.
class UpperAttrMeta(type):
def __new__(mcs, name, bases, namespace):
upper = {k.upper(): v for k, v in namespace.items()
if not k.startswith("_")}
return super().__new__(mcs, name, bases, {**namespace, **upper})
class Config(metaclass=UpperAttrMeta):
debug = True
print(Config.DEBUG) # True__init__ überschreiben
__init__(cls, name, bases, namespace) wird nach der Erstellung der Klasse ausgeführt. Verwenden Sie die Methode für Einrichtungsschritte nach der Erstellung, etwa zur Registrierung.
_registry = {}
class AutoRegisterMeta(type):
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
if bases: # skip the base class itself
_registry[name] = cls
class Handler(metaclass=AutoRegisterMeta): pass
class JSONHandler(Handler): pass
class XMLHandler(Handler): pass
print(_registry) # {'JSONHandler': ..., 'XMLHandler': ...}Schnittstellenverträge durchsetzen
Lösen Sie in __new__ eine TypeError-Ausnahme aus, wenn erforderliche Methoden im Klassenkörper fehlen.
class InterfaceMeta(type):
REQUIRED = frozenset({"process", "validate"})
def __new__(mcs, name, bases, ns):
if bases and not mcs.REQUIRED.issubset(ns):
missing = mcs.REQUIRED - ns.keys()
raise TypeError(f"{name} must define: {missing}")
return super().__new__(mcs, name, bases, ns)
class Worker(metaclass=InterfaceMeta):
def process(self): ...
def validate(self): ...Singleton per Metaklasse
Überschreiben Sie __call__ in der Metaklasse, um die Instanzerstellung abzufangen und jedes Mal dieselbe Instanz zurückzugeben.
class SingletonMeta(type):
_instances = {}
def __call__(cls, *a, **kw):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*a, **kw)
return cls._instances[cls]
class DB(metaclass=SingletonMeta):
pass
print(DB() is DB()) # TrueMethoden dynamisch hinzufügen
Fügen Sie jeder Klasse, die die Metaklasse verwendet, Hilfsmethoden hinzu, ohne jede Klasse einzeln zu ändern.
class MethodInjectorMeta(type):
def __new__(mcs, name, bases, ns):
ns["describe"] = lambda self: f"I am {type(self).__name__}"
return super().__new__(mcs, name, bases, ns)
class Widget(metaclass=MethodInjectorMeta):
pass
print(Widget().describe()) # I am Widget__prepare__ für einen geordneten Namespace
Überschreiben Sie __prepare__, um ein benutzerdefiniertes Mapping als Klassennamespace bereitzustellen, etwa um die Definitionsreihenfolge zu erhalten oder Duplikate zu erkennen.
class NoDuplicatesMeta(type):
class _NS(dict):
def __setitem__(self, k, v):
if k in self and not k.startswith("_"):
raise ValueError(f"Duplicate attribute: {k}")
super().__setitem__(k, v)
@classmethod
def __prepare__(mcs, name, bases):
return mcs._NS()Auflösung von Metaklassenkonflikten
Wenn mehrere Basisklassen unterschiedliche Metaklassen haben, verlangt Python, dass die am weitesten abgeleitete Metaklasse explizit verwendet wird; andernfalls wird ein Konfliktfehler ausgelöst.
class MetaA(type): pass
class MetaB(type): pass
# class Bad(A, B): pass # TypeError: metaclass conflict
class MetaAB(MetaA, MetaB): pass # resolve manually
class A(metaclass=MetaA): pass
class B(metaclass=MetaB): pass
class Good(A, B, metaclass=MetaAB): passMetaklasse vs. __init_subclass__
Für die Registrierung von Unterklassen und einfache Hooks ist __init_subclass__ unkomplizierter und erfordert keine Metaklasse.
class Base:
_registry = []
def __init_subclass__(cls, **kw):
super().__init_subclass__(**kw)
Base._registry.append(cls)
class ChildA(Base): pass
class ChildB(Base): pass
print(Base._registry) # [ChildA, ChildB]Attributzugriffe nachverfolgen
Überschreiben Sie __getattribute__ in der Metaklasse, um Attributzugriffe auf der Klasse abzufangen, nicht auf Instanzen.
class TraceMeta(type):
def __getattribute__(cls, name):
val = super().__getattribute__(name)
if not name.startswith("_"):
print(f"Accessing class attr: {name}")
return val
class Config(metaclass=TraceMeta):
HOST = "localhost"
_ = Config.HOST # logs: Accessing class attr: HOSTMuster zum Sammeln von ORM-Feldern
ORMs nach Django-Art verwenden Metaklassen, um Field-Deskriptoren aus dem Klassenkörper in einer _meta-Registry zu sammeln.
class Field:
def __init__(self, col): self.col = col
class ModelMeta(type):
def __new__(mcs, name, bases, ns):
fields = {k: v for k, v in ns.items() if isinstance(v, Field)}
ns["_fields"] = fields
return super().__new__(mcs, name, bases, ns)
class User(metaclass=ModelMeta):
name = Field("name")
email = Field("email")
print(User._fields.keys()) # dict_keys(['name', 'email'])Wissenscheck
Welche Methode wird in einer benutzerdefinierten Metaklasse aufgerufen, um das Klassenobjekt selbst zu erstellen, also vor __init__?
Zusammenfassung
Benutzerdefinierte Metaklassen überschreiben __new__/__init__, um die Klassenerstellung zu steuern: Sie setzen Verträge durch, registrieren Unterklassen automatisch, fügen Methoden hinzu oder sammeln Feld-Deskriptoren. Bevorzugen Sie __init_subclass__ für einfache Anforderungen an die Registrierung.
Häufig gestellte Fragen
Ist die Lektion „Benutzerdefinierte Metaklassen schreiben“ kostenlos?
Ja — der vollständige Text von „Benutzerdefinierte Metaklassen schreiben“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Python Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Python Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Benutzerdefinierte Metaklassen schreiben“?
Verwenden Sie __new__ und __init__ in Metaklassen, um die Klassenerstellung anzupassen. Du übst Python Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Python Academy zu starten?
Keine Vorkenntnisse erforderlich. Python Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.
Wie lange dauert die Lektion „Benutzerdefinierte Metaklassen schreiben“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Python Academy-Lektion Code schreiben und ausführen?
Ja. Jede Python Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Wie Python-Klassen erstellt werden
- Benutzerdefinierte Metaklassen schreiben
- Deskriptoren: __get__, __set__, __delete__
- __slots__ und Speicheroptimierung