0Pricing
Python Academy · Lektion

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())  # True

Methoden 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): pass

Metaklasse 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: HOST

Muster 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

  1. Wie Python-Klassen erstellt werden
  2. Benutzerdefinierte Metaklassen schreiben
  3. Deskriptoren: __get__, __set__, __delete__
  4. __slots__ und Speicheroptimierung
← Zurück zu Python Academy