Skrive egendefinerte metaklasser
Bruk __new__ og __init__ i metaklasser til å tilpasse klasseopprettingen.
Skrive egendefinerte metaklasser er en gratis leksjon i Python Academy på CoddyKit. Dette er leksjon 2 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Python Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Python Academy inneholder totalt 4 leksjoner.
Deklarering av en metaklasse
Send inn metaclass=MyMeta i klassedefinisjonen for å bruke en egendefinert metaklasse. Metaklassen må arve fra type.
class MyMeta(type):
pass
class Foo(metaclass=MyMeta):
pass
print(type(Foo)) # <class '__main__.MyMeta'>Overstyring av __new__
__new__(mcs, name, bases, namespace) oppretter klasseobjektet. Bruk den til å endre navnerommet før klassen bygges.
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) # TrueOverstyring av __init__
__init__(cls, name, bases, namespace) kjører etter at klassen er opprettet. Bruk den til oppsett etter opprettelsen, for eksempel registrering.
_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': ...}Håndheving av grensesnittkontrakter
Utløs TypeError i __new__ hvis nødvendige metoder mangler i klassekroppen.
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 metaklasse
Overstyr __call__ på metaklassen for å fange opp opprettelsen av instanser og returnere den samme instansen hver gang.
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()) # TrueDynamisk tillegging av metoder
Sett inn hjelpemetoder i hver klasse som bruker metaklassen, uten å endre hver enkelt klasse.
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__ for ordnet navnerom
Overstyr __prepare__ for å levere en egendefinert mapping som klassens navnerom, for eksempel for å bevare definisjonsrekkefølgen eller oppdage duplikater.
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()Løsning av metaklassekonflikter
Når flere basisklasser har forskjellige metaklasser, krever Python at den mest avledede metaklassen brukes eksplisitt. Ellers utløses en konfliktfeil.
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 kontra __init_subclass__
For registrering av underklasser og enkle kroker er __init_subclass__ enklere og krever ingen 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]Sporing av attributttilgang
Overstyr __getattribute__ på metaklassen for å fange opp attributttilgang på klassen, ikke på instanser.
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: HOSTMønster for innsamling av ORM-felt
ORM-er i Django-stil bruker metaklasser til å samle Field-deskriptorer fra klassekroppen i et _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'])Hurtigsjekk
I en egendefinert metaklasse, hvilken metode kalles for å opprette selve klasseobjektet, før __init__?
Oppsummering
Egendefinerte metaklasser overstyrer __new__/__init__ for å styre opprettelsen av klasser: håndheve kontrakter, automatisk registrere underklasser, sette inn metoder eller samle feltbeskrivelser. Foretrekk __init_subclass__ når du bare trenger enkel registrering.
Lær deg Python med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 76
- Leksjoner
- 320
Ofte stilte spørsmål
Er leksjonen «Skrive egendefinerte metaklasser» gratis?
Ja – hele teksten i «Skrive egendefinerte metaklasser» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Python Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Python Academy inneholder totalt 4 leksjoner.
Hva lærer jeg i «Skrive egendefinerte metaklasser»?
Bruk __new__ og __init__ i metaklasser til å tilpasse klasseopprettingen. Du øver på Python Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med Python Academy?
Ingen tidligere erfaring er nødvendig. Python Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.
Hvor lang tid tar leksjonen «Skrive egendefinerte metaklasser»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne Python Academy-leksjonen?
Ja. Alle Python Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Hvordan Python-klasser opprettes
- Skrive egendefinerte metaklasser
- Deskriptorer: __get__, __set__, __delete__
- __slots__ og minneoptimalisering