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:

Python 2.x 2.7

È possibile utilizzare un attributo speciale di livello di classe __metaclass__ per specificare il metaclass.

class MyDummy(object):
    __metaclass__ = mytype
type(MyDummy)  # <class '__main__.mytype'>
Python 3.x 3.0

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
Python 2.x 2.7
class MySingleton(object):
    __metaclass__ = SingletonType
Python 3.x 3.0
class MySingleton(metaclass=SingletonType):
    pass
MySingleton() is MySingleton()  # True, only one instantiation occurs

Utilizzando un metaclass

Sintassi del metaclass

Python 2.x 2.7
class MyClass(object):
    __metaclass__ = SomeMetaclass
Python 3.x 3.0
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.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow