python cos'è serve - Cosa sono le metaclassi in Python?




8 Answers

Una metaclasse è la classe di una classe. Come una classe definisce come si comporta un'istanza della classe, una metaclasse definisce come si comporta una classe. Una classe è un'istanza di un metaclasse.

Mentre in Python puoi usare le callable arbitrarie per metaclassi (come gli spettacoli di Jerub ), l'approccio più utile è in realtà quello di renderlo una vera e propria classe. type è il solito metaclasse in Python. Nel caso ti stia chiedendo, sì, il type è di per sé una classe, ed è un suo tipo. Non sarai in grado di ricreare qualcosa come il type esclusivamente in Python, ma Python imbroglia un po '. Per creare il tuo metaclasse in Python devi solo sottoclasse il type .

Un metaclasse è più comunemente usato come una fabbrica di classi. Come si crea un'istanza della classe chiamando la classe, Python crea una nuova classe (quando esegue l'istruzione 'classe') chiamando il metaclasse. In combinazione con i normali metodi __init__ e __new__ , le metaclassi consentono quindi di fare 'cose extra' quando si crea una classe, come registrare la nuova classe con un registro, o addirittura sostituire la classe con qualcos'altro.

Quando viene eseguita l'istruzione della class , Python esegue prima il corpo dell'istruzione della class come un normale blocco di codice. Lo spazio dei nomi risultante (un dict) contiene gli attributi della futura classe. La metaclasse viene determinata osservando i baseclass della classe-to-be (i metaclassi sono ereditati), l'attributo __metaclass__ della classe-to-be (se presente) o la variabile globale __metaclass__ . Il metaclasse viene quindi chiamato con il nome, le basi e gli attributi della classe per istanziarlo.

Tuttavia, le metaclassi in realtà definiscono il tipo di una classe, non solo una fabbrica per essa, quindi puoi fare molto di più con loro. Ad esempio, puoi definire i metodi normali sul metaclass. Questi metodi metaclass sono come i metodi di classe, in quanto possono essere chiamati sulla classe senza un'istanza, ma non sono come i metodi di classe in quanto non possono essere chiamati su un'istanza della classe. type.__subclasses__() è un esempio di un metodo sul metaclass di type . Puoi anche definire i normali metodi "magici", come __add__ , __iter__ e __getattr__ , per implementare o modificare il comportamento della classe.

Ecco un esempio aggregato di bit e pezzi:

def make_hook(f):
    """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(mcls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print "Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print "Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
guida ita

Cosa sono le metaclassi e per cosa le usiamo?




Nota, questa risposta è per Python 2.x come è stato scritto nel 2008, le metaclature sono leggermente diverse in 3.x, vedi i commenti.

I metaclassi sono la salsa segreta che rende il lavoro 'di classe'. Il metaclasse predefinito per un nuovo oggetto stile è chiamato 'tipo'.

class type(object)
  |  type(object) -> the object's type
  |  type(name, bases, dict) -> a new type

I metaclassi richiedono 3 arg. ' name ', ' bases ' e ' dict '

Qui è dove inizia il segreto. Cerca da dove vengono il nome, le basi e il dict in questa definizione di classe di esempio.

class ThisIsTheName(Bases, Are, Here):
    All_the_code_here
    def doesIs(create, a):
        dict

Definiamo un metaclass che dimostrerà come " classe: " lo chiama.

def test_metaclass(name, bases, dict):
    print 'The Class Name is', name
    print 'The Class Bases are', bases
    print 'The dict has', len(dict), 'elems, the keys are', dict.keys()

    return "yellow"

class TestName(object, None, int, 1):
    __metaclass__ = test_metaclass
    foo = 1
    def baz(self, arr):
        pass

print 'TestName = ', repr(TestName)

# output => 
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName =  'yellow'

E ora, un esempio che in realtà significa qualcosa, questo renderà automaticamente le variabili nella lista "attributi" impostate sulla classe e impostate su Nessuna.

def init_attributes(name, bases, dict):
    if 'attributes' in dict:
        for attr in dict['attributes']:
            dict[attr] = None

    return type(name, bases, dict)

class Initialised(object):
    __metaclass__ = init_attributes
    attributes = ['foo', 'bar', 'baz']

print 'foo =>', Initialised.foo
# output=>
foo => None

Si noti che il comportamento magico che "Initalizzato" ottiene avendo la metaclasse init_attributes non viene passato in una sottoclasse di Initalised.

Ecco un esempio ancora più concreto, che mostra come è possibile creare una sottoclasse di "tipo" per creare un metaclasse che esegue un'azione quando viene creata la classe. Questo è piuttosto difficile:

class MetaSingleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
        return cls.instance

 class Foo(object):
     __metaclass__ = MetaSingleton

 a = Foo()
 b = Foo()
 assert a is b



Altri hanno spiegato come funzionano le metaclassi e come si adattano al sistema di tipo Python. Ecco un esempio di ciò per cui possono essere utilizzati. In un framework di test che ho scritto, volevo tenere traccia dell'ordine in cui sono state definite le classi, in modo da poterle successivamente istanziare in questo ordine. Ho trovato più facile farlo usando una metaclasse.

class MyMeta(type):

    counter = 0

    def __init__(cls, name, bases, dic):
        type.__init__(cls, name, bases, dic)
        cls._order = MyMeta.counter
        MyMeta.counter += 1

class MyType(object):              # Python 2
    __metaclass__ = MyMeta

class MyType(metaclass=MyMeta):    # Python 3
    pass

Qualunque cosa sia una sottoclasse di MyType ottiene quindi un attributo di classe _order che registra l'ordine in cui sono state definite le classi.




Cosa sono le metaclassi? Per cosa li usi?

TLDR: un metaclasse crea un'istanza e definisce il comportamento di una classe esattamente come una classe crea un'istanza e definisce il comportamento di un'istanza.

pseudocodice:

>>> Class(...)
instance

Quanto sopra dovrebbe sembrare familiare. Bene, da dove viene la Class ? È un'istanza di un metaclasse (anche pseudocodice):

>>> Metaclass(...)
Class

Nel codice reale, possiamo passare il metaclass di default, type , tutto ciò di cui abbiamo bisogno per istanziare una classe e ottenere una classe:

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>

Mettendolo diversamente

  • Una classe è un'istanza come una metaclasse appartiene a una classe.

    Quando istanziamo un oggetto, otteniamo un'istanza:

    >>> object()                          # instantiation of class
    <object object at 0x7f9069b4e0b0>     # instance
    

    Allo stesso modo, quando definiamo una classe esplicitamente con il metaclass di default, digitiamo, lo istanziamo:

    >>> type('Object', (object,), {})     # instantiation of metaclass
    <class '__main__.Object'>             # instance
    
  • In altre parole, una classe è un'istanza di un metaclasse:

    >>> isinstance(object, type)
    True
    
  • Metti una terza via, una metaclasse è la classe di una classe.

    >>> type(object) == type
    True
    >>> object.__class__
    <class 'type'>
    

Quando si scrive una definizione di classe e Python lo esegue, utilizza una metaclasse per istanziare l'oggetto classe (che, a sua volta, sarà utilizzato per istanziare le istanze di quella classe).

Proprio come possiamo usare le definizioni di classe per cambiare il modo in cui si comportano le istanze di oggetti personalizzati, possiamo usare una definizione di classe metaclasse per cambiare il modo in cui un oggetto di classe si comporta.

Per cosa possono essere usati? Dai docs :

I potenziali usi per i metaclassi sono illimitati. Alcune idee che sono state esplorate includono la registrazione, il controllo dell'interfaccia, la delega automatica, la creazione automatica delle proprietà, i proxy, i framework e il blocco / sincronizzazione automatica delle risorse.

Tuttavia, di solito è incoraggiato gli utenti a evitare l'uso di metaclassi a meno che non sia assolutamente necessario.

Usi un metaclasse ogni volta che crei una classe:

Quando scrivi una definizione di classe, ad esempio, come questa,

class Foo(object): 
    'demo'

Si istanzia un oggetto di classe.

>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

È uguale al type chiamata funzionalmente con gli argomenti appropriati e all'assegnazione del risultato a una variabile di tale nome:

name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

Nota, alcune cose vengono automaticamente aggiunte al __dict__ , cioè lo spazio dei nomi:

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
'__module__': '__main__', '__weakref__': <attribute '__weakref__' 
of 'Foo' objects>, '__doc__': 'demo'})

La metaclasse dell'oggetto che abbiamo creato, in entrambi i casi, è di type .

(Una nota laterale sul contenuto della classe __dict__ : __module__ è lì perché le classi devono sapere dove sono definite, e __dict__ e __weakref__ ci sono perché non definiamo __slots__ - se definiamo __slots__ salveremo un po 'di spazio nelle istanze, poiché è possibile disabilitare __dict__ e __weakref__ escludendole. Ad esempio:

>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

... ma sto divagando.)

Possiamo estendere il type come qualsiasi altra definizione di classe:

Ecco il predefinito __repr__ delle classi:

>>> Foo
<class '__main__.Foo'>

Una delle cose più preziose che possiamo fare di default nella scrittura di un oggetto Python è di fornirgli un buon __repr__ . Quando chiamiamo help(repr) apprendiamo che c'è un buon test per un __repr__ che richiede anche un test per l'uguaglianza - obj == eval(repr(obj)) . La seguente semplice implementazione di __repr__ e __eq__ per le istanze di classe della nostra classe tipo ci fornisce una dimostrazione che può migliorare sul __repr__ predefinito delle classi:

class Type(type):
    def __repr__(cls):
        """
        >>> Baz
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        >>> eval(repr(Baz))
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        """
        metaname = type(cls).__name__
        name = cls.__name__
        parents = ', '.join(b.__name__ for b in cls.__bases__)
        if parents:
            parents += ','
        namespace = ', '.join(': '.join(
          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
               for k, v in cls.__dict__.items())
        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
    def __eq__(cls, other):
        """
        >>> Baz == eval(repr(Baz))
        True            
        """
        return (cls.__name__, cls.__bases__, cls.__dict__) == (
                other.__name__, other.__bases__, other.__dict__)

Così ora, quando creiamo un oggetto con questo metaclasse, l' __repr__echo sulla riga di comando fornisce una vista molto meno brutta del default:

>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

Con una __repr__definizione definita per l'istanza della classe, abbiamo una maggiore capacità di eseguire il debug del nostro codice. Tuttavia, è molto eval(repr(Class))improbabile effettuare ulteriori verifiche (in quanto le funzioni sarebbero piuttosto impossibili da valutare rispetto alle impostazioni predefinite __repr__).

Un utilizzo previsto: __prepare__uno spazio dei nomi

Se, per esempio, vogliamo sapere in quale ordine vengono creati i metodi di una classe, potremmo fornire un ditt ordinato come lo spazio dei nomi della classe. Lo faremo con il __prepare__quale restituisce lo spazio dei nomi dettato per la classe se è implementato in Python 3 :

from collections import OrderedDict

class OrderedType(Type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return OrderedDict()
    def __new__(cls, name, bases, namespace, **kwargs):
        result = Type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

E l'uso:

class OrderedMethodsObject(object, metaclass=OrderedType):
    def method1(self): pass
    def method2(self): pass
    def method3(self): pass
    def method4(self): pass

E ora abbiamo una registrazione dell'ordine in cui sono stati creati questi metodi (e altri attributi di classe):

>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

Nota, questo esempio è stato adattato dalla docs : il nuovo enum nella libreria standard lo fa.

Quindi quello che abbiamo fatto è stato creare un'istanza di un metaclass creando una classe. Possiamo anche trattare il metaclass come faremmo con qualsiasi altra classe. Ha un ordine di risoluzione del metodo:

>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

E ha approssimativamente il corretto repr(che non possiamo più valutare se non siamo in grado di trovare un modo per rappresentare le nostre funzioni.):

>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})



Ruolo del __call__()metodo di un metaclasse durante la creazione di un'istanza di classe

Se hai fatto la programmazione Python per più di qualche mese, alla fine ti imbatterai in codice simile a questo:

# define a class
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

# create an instance of it
instance = SomeClass()

# then call the object as if it's a function
result = instance('foo', 'bar')

Quest'ultimo è possibile quando si implementa il __call__()metodo magico sulla classe.

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

Il __call__()metodo viene invocato quando un'istanza di una classe viene utilizzata come callable. Ma come abbiamo visto dalle risposte precedenti una classe stessa è un'istanza di un metaclasse, quindi quando usiamo la classe come callable (cioè quando ne creiamo un'istanza) in realtà chiamiamo il __call__()metodo del metaclasse . A questo punto la maggior parte dei programmatori Python sono un po 'confusi perché gli è stato detto che quando si crea un'istanza come questa, instance = SomeClass()si sta chiamando il suo __init__()metodo. Alcuni che hanno scavato un po 'più a fondo lo sanno prima che __init__()ci sia __new__(). Bene, oggi viene rivelato un altro strato di verità, prima che __new__()ci sia il metaclass ' __call__().

Studiamo la catena di chiamate del metodo specificatamente dalla prospettiva di creare un'istanza di una classe.

Questo è un metaclasse che registra esattamente il momento prima della creazione di un'istanza e del momento in cui è in procinto di restituirlo.

class Meta_1(type):
    def __call__(cls):
        print "Meta_1.__call__() before creating an instance of ", cls
        instance = super(Meta_1, cls).__call__()
        print "Meta_1.__call__() about to return instance."
        return instance

Questa è una classe che usa quel metaclasse

class Class_1(object):

    __metaclass__ = Meta_1

    def __new__(cls):
        print "Class_1.__new__() before creating an instance."
        instance = super(Class_1, cls).__new__(cls)
        print "Class_1.__new__() about to return instance."
        return instance

    def __init__(self):
        print "entering Class_1.__init__() for instance initialization."
        super(Class_1,self).__init__()
        print "exiting Class_1.__init__()."

E ora creiamo un'istanza di Class_1

instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

Osservare che il codice sopra non fa in realtà nulla di più che registrare le attività. Ogni metodo delega il lavoro effettivo all'implementazione del genitore, mantenendo così il comportamento predefinito. Poiché typeè Meta_1la classe genitore (che typeè il metaclass principale genitore predefinito) e considerando la sequenza di ordinamento dell'output sopra, ora abbiamo un indizio su quale sarebbe la pseudo implementazione di type.__call__():

class type:
    def __call__(cls, *args, **kwarg):

        # ... maybe a few things done to cls here

        # then we call __new__() on the class to create an instance
        instance = cls.__new__(cls, *args, **kwargs)

        # ... maybe a few things done to the instance here

        # then we initialize the instance with its __init__() method
        instance.__init__(*args, **kwargs)

        # ... maybe a few more things done to instance here

        # then we return it
        return instance

Possiamo vedere che il __call__()metodo del metaclasse è quello che viene chiamato per primo. Quindi delega la creazione dell'istanza al __new__()metodo della classe e l'inizializzazione all'istanza __init__(). È anche quello che alla fine restituisce l'istanza.

Da quanto precede deriva che al metaclass ' __call__()viene anche data l'opportunità di decidere se una chiamata Class_1.__new__()o se Class_1.__init__()sarà eventualmente effettuata. Nel corso della sua esecuzione potrebbe effettivamente restituire un oggetto che non è stato toccato da nessuno di questi metodi. Prendiamo ad esempio questo approccio al modello singleton:

class Meta_2(type):
    singletons = {}

    def __call__(cls, *args, **kwargs):
        if cls in Meta_2.singletons:
            # we return the only instance and skip a call to __new__()
            # and __init__()
            print ("{} singleton returning from Meta_2.__call__(), "
                   "skipping creation of new instance.".format(cls))
            return Meta_2.singletons[cls]

        # else if the singleton isn't present we proceed as usual
        print "Meta_2.__call__() before creating an instance."
        instance = super(Meta_2, cls).__call__(*args, **kwargs)
        Meta_2.singletons[cls] = instance
        print "Meta_2.__call__() returning new instance."
        return instance

class Class_2(object):

    __metaclass__ = Meta_2

    def __new__(cls, *args, **kwargs):
        print "Class_2.__new__() before creating instance."
        instance = super(Class_2, cls).__new__(cls)
        print "Class_2.__new__() returning instance."
        return instance

    def __init__(self, *args, **kwargs):
        print "entering Class_2.__init__() for initialization."
        super(Class_2, self).__init__()
        print "exiting Class_2.__init__()."

Osserviamo cosa succede quando ripetutamente proviamo a creare un oggetto di tipo Class_2

a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.

b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True



typeè in realtà un metaclass- una classe che crea un'altra classe. La maggior parte metaclasssono le sottoclassi di type. La metaclassriceve la newclasse come primo parametro e fornire accesso all'oggetto classe con dettagli come indicato di seguito:

>>> class MetaClass(type):
...     def __init__(cls, name, bases, attrs):
...         print ('class name: %s' %name )
...         print ('Defining class %s' %cls)
...         print('Bases %s: ' %bases)
...         print('Attributes')
...         for (name, value) in attrs.items():
...             print ('%s :%r' %(name, value))
... 

>>> class NewClass(object, metaclass=MetaClass):
...    get_choch='dairy'
... 
class name: NewClass
Bases <class 'object'>: 
Defining class <class 'NewClass'>
get_choch :'dairy'
__module__ :'builtins'
__qualname__ :'NewClass'

Note:

Si noti che la classe non è stata istanziata in qualsiasi momento; il semplice atto di creare la classe ha innescato l'esecuzione del metaclass.




Le classi Python sono esse stesse oggetti - come in esempio - della loro meta-classe.

Il metaclasse predefinito, che viene applicato quando si determinano le classi come:

class foo:
    ...

la meta classe viene utilizzata per applicare alcune regole a un intero insieme di classi. Ad esempio, supponiamo che stai creando un ORM per accedere a un database e desideri che i record di ogni tabella siano di una classe mappata a quella tabella (basata su campi, regole aziendali, ecc.), Un possibile uso di metaclasse è ad esempio la logica del pool di connessioni, che è condivisa da tutte le classi di record di tutte le tabelle. Un altro uso è logico per supportare chiavi esterne, che coinvolgono più classi di record.

quando definisci il metaclasse, inserisci un tipo di sottoclasse e puoi eseguire l'override dei seguenti metodi magici per inserire la tua logica.

class somemeta(type):
    __new__(mcs, name, bases, clsdict):
      """
  mcs: is the base metaclass, in this case type.
  name: name of the new class, as provided by the user.
  bases: tuple of base classes 
  clsdict: a dictionary containing all methods and attributes defined on class

  you must return a class object by invoking the __new__ constructor on the base metaclass. 
 ie: 
    return type.__call__(mcs, name, bases, clsdict).

  in the following case:

  class foo(baseclass):
        __metaclass__ = somemeta

  an_attr = 12

  def bar(self):
      ...

  @classmethod
  def foo(cls):
      ...

      arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}

      you can modify any of these values before passing on to type
      """
      return type.__call__(mcs, name, bases, clsdict)


    def __init__(self, name, bases, clsdict):
      """ 
      called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
      """
      pass


    def __prepare__():
        """
        returns a dict or something that can be used as a namespace.
        the type will then attach methods and attributes from class definition to it.

        call order :

        somemeta.__new__ ->  type.__new__ -> type.__init__ -> somemeta.__init__ 
        """
        return dict()

    def mymethod(cls):
        """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
        """
        pass

comunque, questi due sono i ganci più comunemente usati. il metaclassing è potente e al di sopra non esiste un elenco esauriente e completo di usi per il metaclassing.




Oltre alle risposte pubblicate posso dire che metaclassdefinisce il comportamento di una classe. Quindi, puoi impostare esplicitamente il tuo metaclasse. Ogni volta che Python ottiene una parola chiave class, inizia a cercare il metaclass. Se non è stato trovato - il tipo di metaclasse predefinito viene utilizzato per creare l'oggetto della classe. Utilizzando l' __metaclass__attributo, puoi impostare la metaclasstua classe:

class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

Produrrà l'output in questo modo:

class 'type'

E, naturalmente, puoi crearne uno metaclassper definire il comportamento di qualsiasi classe creata usando la tua classe.

Per fare ciò, la metaclassclasse di tipo di default deve essere ereditata in quanto questa è la principale metaclass:

class MyMetaClass(type):
   __metaclass__ = type
   # you can write here any behaviour you want

class MyTestClass:
   __metaclass__ = MyMetaClass

Obj = MyTestClass()
print(Obj.__metaclass__)
print(MyMetaClass.__metaclass__)

L'output sarà:

class '__main__.MyMetaClass'
class 'type'



Related