Scrittura di metaclassi personalizzate
Utilizzi __new__ e __init__ nelle metaclassi per personalizzare la creazione delle classi.
Scrittura di metaclassi personalizzate è una lezione Python Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Python Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Python Academy include 4 lezioni in totale.
Dichiarazione di una metaclasse
Passi metaclass=MyMeta nella definizione della classe per utilizzare una metaclasse personalizzata. La metaclasse deve ereditare da type.
class MyMeta(type):
pass
class Foo(metaclass=MyMeta):
pass
print(type(Foo)) # <class '__main__.MyMeta'>Sovrascrittura di __new__
__new__(mcs, name, bases, namespace) crea l'oggetto classe. Lo utilizzi per modificare lo spazio dei nomi prima della costruzione della classe.
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) # TrueSovrascrittura di __init__
__init__(cls, name, bases, namespace) viene eseguito dopo la creazione della classe. Lo utilizzi per la configurazione successiva alla creazione, ad esempio la registrazione.
_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': ...}Imposizione dei contratti dell'interfaccia
Sollevi TypeError in __new__ se nel corpo della classe mancano i metodi richiesti.
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 tramite metaclasse
Sovrascriva __call__ nella metaclasse per intercettare la creazione delle istanze e restituire ogni volta la stessa istanza.
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()) # TrueAggiunta dinamica di metodi
Inserisca metodi di utilità in ogni classe che utilizza la metaclasse, senza modificare singolarmente ciascuna classe.
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__ per uno spazio dei nomi ordinato
Sovrascriva __prepare__ per fornire una mappatura personalizzata come spazio dei nomi della classe, ad esempio per conservare l'ordine delle definizioni o rilevare i duplicati.
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()Risoluzione dei conflitti tra metaclassi
Quando più classi base hanno metaclassi diverse, Python richiede l'utilizzo esplicito della metaclasse più derivata, altrimenti solleva un errore di conflitto.
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): passMetaclasse vs __init_subclass__
Per la registrazione delle sottoclassi e gli hook semplici, __init_subclass__ è più semplice e non richiede una metaclasse.
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]Tracciamento dell'accesso agli attributi
Sovrascriva __getattribute__ nella metaclasse per intercettare l'accesso agli attributi della classe, non delle istanze.
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: HOSTPattern di raccolta dei campi ORM
Gli ORM in stile Django utilizzano le metaclassi per raccogliere i descriptor Field dal corpo della classe in un registro _meta.
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'])Verifica rapida
In una metaclasse personalizzata, quale metodo viene chiamato per creare l'oggetto classe stesso, prima di __init__?
Riepilogo
Le metaclassi personalizzate sovrascrivono __new__/__init__ per controllare la creazione delle classi: impongono contratti, registrano automaticamente le sottoclassi, inseriscono metodi o raccolgono descriptor di campo. Preferisca __init_subclass__ per le semplici esigenze di registrazione.
Domande Frequenti
La lezione «Scrittura di metaclassi personalizzate» è gratuita?
Sì — il testo completo di «Scrittura di metaclassi personalizzate» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Python Academy, passa a CoddyKit PRO. Il corso Python Academy include 4 lezioni in totale.
Cosa imparerò in «Scrittura di metaclassi personalizzate»?
Utilizzi __new__ e __init__ nelle metaclassi per personalizzare la creazione delle classi. Eserciti Python Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Python Academy?
Non è richiesta alcuna esperienza precedente. Python Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Scrittura di metaclassi personalizzate»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Python Academy?
Sì. Ogni lezione Python Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Come vengono create le classi Python
- Scrittura di metaclassi personalizzate
- Descriptor: __get__, __set__, __delete__
- __slots__ e ottimizzazione della memoria