Python Language
metaclassi
Ricerca…
introduzione
Le metaclassi ti permettono di modificare profondamente il comportamento delle classi Python (in termini di come sono definite, istanziate, accessibili e altro) sostituendo la metaclasse di type
che le nuove classi usano di default.
Osservazioni
Quando progettate la vostra architettura, considerate che molte cose che possono essere compiute con le metaclassi possono anche essere realizzate usando semantica più semplice:
- L'ereditarietà tradizionale è spesso più che sufficiente.
- I decoratori di classe possono integrare le funzionalità in lezioni con un approccio ad hoc.
- Python 3.6 introduce
__init_subclass__()
che consente a una classe di partecipare alla creazione della sua sottoclasse.
Metaclasses di base
Quando il type
viene chiamato con tre argomenti, si comporta come la (meta) classe che è, e crea una nuova istanza, es. produce una nuova classe / tipo.
Dummy = type('OtherDummy', (), dict(x=1))
Dummy.__class__ # <type 'type'>
Dummy().__class__.__class__ # <type 'type'>
È possibile sottoclasse il type
per creare un metaclass personalizzato.
class mytype(type):
def __init__(cls, name, bases, dict):
# call the base initializer
type.__init__(cls, name, bases, dict)
# perform custom initialization...
cls.__custom_attribute__ = 2
Ora abbiamo un nuovo metaclasse mytype
personalizzato che può essere utilizzato per creare classi nello stesso modo del type
.
MyDummy = mytype('MyDummy', (), dict(x=2))
MyDummy.__class__ # <class '__main__.mytype'>
MyDummy().__class__.__class__ # <class '__main__.mytype'>
MyDummy.__custom_attribute__ # 2
Quando creiamo una nuova classe usando la parola chiave della class
il metaclasse viene scelto di default in base alle baseclass.
>>> class Foo(object):
... pass
>>> type(Foo)
type
Nell'esempio precedente l'unica classe di base è l' object
quindi il nostro metaclasse sarà il tipo di object
, che è il type
. È possibile sovrascrivere il valore predefinito, tuttavia dipende se utilizziamo Python 2 o Python 3:
È possibile utilizzare un attributo speciale di livello di classe __metaclass__
per specificare il metaclass.
class MyDummy(object):
__metaclass__ = mytype
type(MyDummy) # <class '__main__.mytype'>
Un argomento specifico per la parola chiave metaclass
specifica il metaclass.
class MyDummy(metaclass=mytype):
pass
type(MyDummy) # <class '__main__.mytype'>
Tutti gli argomenti delle parole chiave (tranne il metaclass
) nella dichiarazione della classe verranno passati al metaclasse. Quindi la class MyDummy(metaclass=mytype, x=2)
passerà x=2
come argomento della parola chiave al costruttore mytype
.
Leggi questa descrizione approfondita delle meta-classi Python per maggiori dettagli.
Singleton usando metaclassi
Un singleton è un pattern che limita l'istanza di una classe a un'istanza / oggetto. Per maggiori informazioni sui modelli di design singleton Python, vedere qui .
class SingletonType(type):
def __call__(cls, *args, **kwargs):
try:
return cls.__instance
except AttributeError:
cls.__instance = super(SingletonType, cls).__call__(*args, **kwargs)
return cls.__instance
class MySingleton(object):
__metaclass__ = SingletonType
class MySingleton(metaclass=SingletonType):
pass
MySingleton() is MySingleton() # True, only one instantiation occurs
Utilizzando un metaclass
Sintassi del metaclass
class MyClass(object):
__metaclass__ = SomeMetaclass
class MyClass(metaclass=SomeMetaclass):
pass
Compatibilità con Python 2 e 3 con six
import six
class MyClass(six.with_metaclass(SomeMetaclass)):
pass
Funzionalità personalizzate con metaclassi
Le funzionalità delle metaclassi possono essere modificate in modo che ogni volta che viene creata una classe, una stringa venga stampata sullo standard output o venga generata un'eccezione. Questo metaclasse stamperà il nome della classe in costruzione.
class VerboseMetaclass(type):
def __new__(cls, class_name, class_parents, class_dict):
print("Creating class ", class_name)
new_class = super().__new__(cls, class_name, class_parents, class_dict)
return new_class
Puoi usare la metaclatura in questo modo:
class Spam(metaclass=VerboseMetaclass):
def eggs(self):
print("[insert example string here]")
s = Spam()
s.eggs()
Lo standard output sarà:
Creating class Spam
[insert example string here]
Introduzione ai metaclassi
Cos'è un metaclass?
In Python, tutto è un oggetto: interi, stringhe, liste, persino funzioni e classi stesse sono oggetti. E ogni oggetto è un'istanza di una classe.
Per verificare la classe di un oggetto x, si può chiamare type(x)
, quindi:
>>> type(5)
<type 'int'>
>>> type(str)
<type 'type'>
>>> type([1, 2, 3])
<type 'list'>
>>> class C(object):
... pass
...
>>> type(C)
<type 'type'>
La maggior parte delle classi in python sono istanze di type
. type
stesso è anche una classe. Tali classi le cui istanze sono anche classi sono chiamate metaclassi.
Il Metaclass più semplice
OK, quindi c'è già un metaclasse in Python: type
. Possiamo crearne un altro?
class SimplestMetaclass(type):
pass
class MyClass(object):
__metaclass__ = SimplestMetaclass
Questo non aggiunge alcuna funzionalità, ma è un nuovo metaclasse, vedi che MyClass è ora un'istanza di SimplestMetaclass:
>>> type(MyClass)
<class '__main__.SimplestMetaclass'>
Un Metaclass che fa qualcosa
Una metaclasse che fa qualcosa di solito sovrascrive il type
__new__
, per modificare alcune proprietà della classe da creare, prima di chiamare l' __new__
originale che crea la classe:
class AnotherMetaclass(type):
def __new__(cls, name, parents, dct):
# cls is this class
# name is the name of the class to be created
# parents is the list of the class's parent classes
# dct is the list of class's attributes (methods, static variables)
# here all of the attributes can be modified before creating the class, e.g.
dct['x'] = 8 # now the class will have a static variable x = 8
# return value is the new class. super will take care of that
return super(AnotherMetaclass, cls).__new__(cls, name, parents, dct)
Il metaclass predefinito
Potresti aver sentito che tutto in Python è un oggetto. È vero, e tutti gli oggetti hanno una classe:
>>> type(1)
int
Il letterale 1 è un'istanza di int
. Dichiariamo una classe:
>>> class Foo(object):
... pass
...
Ora consente di istanziarlo:
>>> bar = Foo()
Qual è la classe di bar
?
>>> type(bar)
Foo
Bello, il bar
è un'istanza di Foo
. Ma qual è la classe di Foo
stessa?
>>> type(Foo)
type
Ok, Foo
stesso è un'istanza di type
. Che ne dici del type
stesso?
>>> type(type)
type
Allora, cos'è un metaclass? Per ora, facciamo finta che sia solo un nome di fantasia per la classe di una classe. Takeaways:
- Tutto è un oggetto in Python, quindi tutto ha una classe
- La classe di una classe è chiamata metaclasse
- Il metaclass predefinito è
type
e di gran lunga è il metaclass più comune
Ma perché dovresti sapere delle metaclassi? Bene, Python stesso è abbastanza "hackerabile", e il concetto di metaclasse è importante se stai facendo cose avanzate come la meta-programmazione o se vuoi controllare come vengono inizializzate le tue classi.