Che cosa sono le metaclassi in Python? A che cosa servono? Lo scopriamo nelle righe di questo articolo!
Se stai affrontando la programmazione in Python, è altamente probabile che tu ti sia imbattuto nelle metaclassi o se vogliamo dirla in inglese le metaclasses.
Bene, in questo articolo avrò il piacere di farti scoprire che cosa sono le metaclasses in Python ma soprattutto a che cosa servono.
Ciao mi chiamo Lorenzo Neri e sono un informatico: realizzo contenuti per aiutare le persone a padroneggiare l’arte del nuovo millennio, ovvero l’informatica!
Metaclassi in Python: un inception
No tranquillo: non sto parlando del film, ma se devo darti una definizione terra-terra di che cosa sono le metaclassi beh… Eccola qua:
Le metaclassi in Python sono le classi di un’altra classe
cit. Leonardo Di Caprio
No, Di Caprio non si è espresso a riguardo, ma sì le metaclassi sono proprio questo: cerchiamo di andare oltre.
Se guardami le cose da un’altra prospettiva, se una classe, a prescindere da Python intendo in qualsiasi linguaggio, definisce come si comporta una sua istanza, beh… Una metaclasse definisce come una classe si comporta.
Troppo tecnichese, sì lo ammetto: cerchiamo di capire qualcosa di più.
In linea generale una metaclasse viene utilizzata come “class-factory“.
Le metaclassi sono “fattorie di classi”: in che senso?
Permettimi di farti un esempio.
Quando crei un oggetto, ovviamente invocando il costruttore della classe in merito, Python crea una nuova classe. Come? Chiamando la sua metaclasse.
Quando lo statement class viene eseguito in questo processo, quello che fa Python è eseguire il corpo della classe come un pezzo di codice normale.
Arrivati a questo punto, il namespace che ne risulta (un dizionario), mantiene in memoria l’attributo della classe che dovrà creare.
In questo processo, la metaclasse è determinata come? Guardando la classe che dovrà creare in questo processo.
Detta in termini molto più semplici, le metaclassi in Python sono un modo per definire il tipo di una classe.
Per questo il discorso di “inception” fatto all’inizio. Ma andiamo oltre per capire ancora di più tutto quanto il discorso.
Cosa posso fare con una metaclasse in Python?
Chiarito il che cosa è una metaclasse, è giusto capire anche che cosa ci puoi fare.
Per esempio, puoi definire dei metodi nella metaclasse.
Questi metodi, che appartengono alla metaclasse, sono metodi che tu puoi chiamare senza avere un’istanza della classe da cui li hai presi!
Chiaramente però, non funzionano come se avessi in mano un’istanza della classe stessa.
Per intenderci se volessi invocare uno di questi metodi, dovresti farlo in questo modo:
metaclasse.__subclasses__()
E puoi usare anche gli altri metodi magici come “__add__”, “__iter__” e ovviamente “__getattr__”.
Ma prima di salutarti, permettimi di lasciarti un esempio completo che riassuma sia che cosa sono le metaclassi in Python, ma anche come funzionano con del codice.
def fai_qualcosa(f):
"""Decorator per cambiare il metodo 'metodo' method into '__metodo__'"""
f.is_qualcosa = 1
return f
class IlMiotipo(type):
def __new__(mcls, name, bases, attrs):
if name.startswith('None'):
return None
# Prendiamo gli attributi e vediamo se devono essere rinominati.
newattrs = {}
for attrname, attrvalue in attrs.iteritems():
if getattr(attrvalue, 'is_qualcosa', 0):
newattrs['__%s__' % attrname] = attrvalue
else:
newattrs[attrname] = attrvalue
return super(IlMiotipo, mcls).__new__(mcls, name, bases, newattrs)
def __init__(self, name, bases, attrs):
super(IlMiotipo, self).__init__(name, bases, attrs)
# classregistry.register(self, self.interfaces)
print "Registriamo la classe %s." % self
def __add__(self, other):
class AutoClass(self, other):
pass
return AutoClass
# Diversamente, per generare automaticamente il classname:
# return type(self.__name__ + other.__name__, (self, other), {})
def unregister(self):
# classregistry.unregister(self)
print "De-registro la classe %s." % self
class IlMioOggetto:
__metaclass__ = IlMiotipo
class NoneSample(IlMioOggetto):
pass
# Stamperà "NoneType None"
print type(NoneSample), repr(NoneSample)
class Esempio(IlMioOggetto):
def __init__(self, value):
self.value = value
@fai_qualcosa
def add(self, other):
return self.__class__(self.value + other.value)
# De-registra la classe
Esempio.unregister()
inst = Esempio(10)
# Fallirà con un errore
#inst.unregister()
print inst + inst
class Parziale(IlMioOggetto):
pass
ExampleSibling = Esempio + Parziale
# ExampleSibling è ora una subclasse sia di "Esempio" sia di "Parziale" anche se pensa di essere invocato come "AutoClass"
print ExampleSibling
print ExampleSibling.__mro__