Python Academy · Les

Aangepaste metaklassen schrijven

Gebruik __new__ en __init__ in metaklassen om het maken van klassen aan te passen.

Les 2 van 413 stappen

Aangepaste metaklassen schrijven is een gratis Python Academy-les op CoddyKit. Dit is les 2 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Python Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Python Academy bevat in totaal 4 lessen.

Een metaklasse declareren

Geef metaclass=MyMeta op in de klassendefinitie om een aangepaste metaklasse te gebruiken. De metaklasse moet overerven van type.

class MyMeta(type):
    pass

class Foo(metaclass=MyMeta):
    pass

print(type(Foo))   # <class '__main__.MyMeta'>

__new__ overschrijven

__new__(mcs, name, bases, namespace) maakt het klasseobject. Gebruik deze om de naamruimte aan te passen voordat de klasse wordt opgebouwd.

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__ overschrijven

__init__(cls, name, bases, namespace) wordt uitgevoerd nadat de klasse is aangemaakt. Gebruik deze voor instellingen na het aanmaken, zoals registratie.

_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': ...}

Interfacecontracten afdwingen

Gooi TypeError op in __new__ als vereiste methoden ontbreken in de klassebody.

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 via een metaklasse

Overschrijf __call__ op de metaklasse om het aanmaken van instanties te onderscheppen en elke keer dezelfde instantie te retourneren.

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

Dynamisch methoden toevoegen

Voeg hulmethoden in elke klasse in die de metaklasse gebruikt, zonder elke klasse afzonderlijk aan te passen.

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__ voor een geordende naamruimte

Overschrijf __prepare__ om een aangepaste toewijzing als klassenaamruimte aan te leveren, bijvoorbeeld om de volgorde van definities te behouden of duplicaten te detecteren.

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

Conflicten tussen metaklassen oplossen

Wanneer meerdere basisklassen verschillende metaklassen hebben, vereist Python dat de meest specifieke metaklasse expliciet wordt gebruikt; anders wordt een conflictfout opgegooid.

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 versus __init_subclass__

Voor registratie van subklassen en eenvoudige haken is __init_subclass__ eenvoudiger en is geen metaklasse nodig.

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]

Toegang tot attributen traceren

Overschrijf __getattribute__ op de metaklasse om toegang tot attributen op de klasse te onderscheppen (niet op instanties).

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

Patroon voor het verzamelen van ORM-velden

ORM's in Django-stijl gebruiken metaklassen om Field-descriptors uit de klassebody te verzamelen in een _meta-register.

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'])

Korte controle

Welke methode wordt in een aangepaste metaklasse aangeroepen om het klasseobject zelf te maken (vóór __init__)?

Samenvatting

Aangepaste metaklassen overschrijven __new__/__init__ om het aanmaken van klassen te sturen: contracten afdwingen, subklassen automatisch registreren, methoden invoegen of veld-descriptors verzamelen. Geef voor eenvoudige registratiebehoeften de voorkeur aan __init_subclass__.

Gratis beginnen

Leer Python met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
76
Lessen
320

Veelgestelde vragen

Is de les “Aangepaste metaklassen schrijven” gratis?

Ja — de volledige tekst van “Aangepaste metaklassen schrijven” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Python Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Python Academy bevat in totaal 4 lessen.

Wat leer ik in “Aangepaste metaklassen schrijven”?

Gebruik __new__ en __init__ in metaklassen om het maken van klassen aan te passen. Je oefent met Python Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Python Academy te beginnen?

Ervaring vooraf is niet nodig. Python Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “Aangepaste metaklassen schrijven”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Python Academy?

Ja. Elke les over Python Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Hoe Python-klassen worden gemaakt
  2. Aangepaste metaklassen schrijven
  3. Descriptors: __get__, __set__, __delete__
  4. __slots__ en geheugenoptimalisatie
← Terug naar Python Academy