Domanda

Ho un amico a cui piace usare le metaclassi e le offre regolarmente come soluzione.

Sono dell'idea che non sia quasi mai necessario utilizzare le metaclassi.Perché?perché immagino che se stai facendo qualcosa del genere a una classe, probabilmente dovresti farlo a un oggetto.Ed è necessaria una piccola riprogettazione/refactoring.

Essere in grado di utilizzare le metaclassi ha portato molte persone in molti posti a utilizzare le classi come una sorta di oggetto di second'ordine, il che mi sembra semplicemente disastroso.La programmazione sarà sostituita dalla metaprogrammazione?L'aggiunta dei decoratori di classe purtroppo lo ha reso ancora più accettabile.

Quindi, per favore, desidero disperatamente conoscere i tuoi casi d'uso validi (concreti) per le metaclassi in Python.O per essere chiariti sul motivo per cui, a volte, mutare le classi è meglio che mutare gli oggetti.

Inizierò:

A volte quando si utilizza una libreria di terze parti è utile essere in grado di mutare la classe in un certo modo.

(Questo è l'unico caso che mi viene in mente e non è concreto)

È stato utile?

Soluzione

Ho una classe che gestisce complotto non interattivo, come un frontend per Matplotlib. Tuttavia, a volte si vuole fare tracciato interattivo. Con solo un paio di funzioni ho scoperto che ero in grado di incrementare il conteggio figura, chiamare disegnare manualmente, ecc, ma avevo bisogno di fare queste prima e dopo ogni chiamata la stampa. Quindi, per creare sia un wrapper tracciato interattivo e un tramando involucro fuori campo, ho scoperto che era più efficiente di fare questo tramite metaclassi, avvolgendo i metodi appropriati, piuttosto che fare qualcosa di simile:

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

Questo metodo non tenere il passo con i cambiamenti API e così via, ma uno che scorre oltre la classe attributi in __init__ prima di ri-impostare gli attributi della classe è più efficiente e mantiene le cose fino ad oggi:

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

Naturalmente, ci potrebbero essere modi migliori per fare questo, ma ho trovato questo per essere efficace. Naturalmente, questo potrebbe anche essere fatto in __new__ o __init__, ma questa era la soluzione che ho trovato il più semplice.

Altri suggerimenti

mi è stato chiesto la stessa domanda di recente, e si avvicinò con più risposte. Spero che sia OK per rilanciare questa discussione, come ho voluto approfondire alcuni dei casi d'uso citati, e aggiungere un paio di nuovi.

La maggior parte delle metaclassi che ho visto fare una delle due cose:

  1. Registrazione (l'aggiunta di una classe per una struttura di dati):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Ogni volta che si Model sottoclasse, la classe è registrato nel dizionario models:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    Questo può essere fatto anche con decoratori di classe:

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    O con una funzione di registrazione esplicito:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

    In realtà, questo è più o meno lo stesso:. Si parla decoratori di classe sfavorevole, ma è davvero nulla di più di zucchero sintattico per un'invocazione funzione su una classe, quindi non c'è nessuna magia su di esso

    In ogni caso, il vantaggio di metaclassi in questo caso è eredità, mentre lavorano per le sottoclassi, mentre le altre soluzioni funzionano solo per le sottoclassi esplicitamente decorati o registrati.

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. Il refactoring (modificando gli attributi di classe o l'aggiunta di nuove):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Ogni volta che si sottoclasse Model e definire alcuni attributi Field, vengono iniettati con i loro nomi (per i messaggi di errore più informativo, per esempio), e raggruppati in un dizionario _fields (per una facile iterazione, senza dover guardare attraverso tutti gli attributi della classe e tutte le sue classi base attributi ogni volta):

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    Ancora una volta, questo può essere fatto (senza eredità) con un decoratore di classe:

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    o esplicitamente:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    

    Anche se, al contrario, per il vostro sostegno per leggibile e mantenibile non-meta di programmazione, questo è molto più ingombrante, ridondante e soggetto ad errori:

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

Dopo aver esaminato i casi di utilizzo più comuni e concreti, gli unici casi in cui è assolutamente necessario utilizzare metaclassi sono quando si desidera modificare il nome della classe o un elenco di classi di base, perché una volta definiti, questi parametri sono cotte nella classe, e nessun decoratore o la funzione li può unbake.

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

Questo può essere utile in contesti di rilascio avvisi ogni volta che vengono definite le classi con nomi simili o alberi di ereditarietà incompleti, ma non riesco a pensare ad una ragione a fianco di pesca a traina di cambiare in realtà questi valori. Forse David Beazley can.

In ogni caso, in Python 3, metaclassi hanno anche il metodo __prepare__, che consente di valutare il corpo classe in una mappatura diversa da una dict, sostenendo in tal modo gli attributi ordinati, gli attributi sovraccaricati, e altre cose interessanti malvagio:

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

Si potrebbe obiettare ordinato attributi possono essere raggiunti con i contatori di creazione, e sovraccarico possono essere simulati con argomenti di default:

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

Oltre ad essere molto più brutto, è anche meno flessibile: cosa succede se si desidera attributi letterali ordinate, come numeri interi e stringhe? Che cosa succede se None è un valore valido per x?

Ecco un modo creativo per risolvere il primo problema:

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

Ed ecco un modo creativo per risolvere il secondo:

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

Ma questo è molto, molto voodoo-er di una semplice metaclasse (specialmente il primo, che si scioglie in realtà il cervello). Il mio punto è, si guarda metaclassi come sconosciuto e contro-intuitivo, ma si può anche guardare a loro come il prossimo passo dell'evoluzione nei linguaggi di programmazione: basta registrare la vostra mentalità. Dopo tutto, probabilmente si potrebbe fare tutto in C, tra cui la definizione di una struttura con puntatori a funzione e passando come primo argomento alle sue funzioni. Una persona che vede C ++ per la prima volta potrebbe dire, "che cosa è questa magia? Perché il compilatore implicitamente passando this a metodi, ma non per le funzioni regolari e statiche? E 'meglio essere esplicito e dettagliato sui tuoi argomenti" Ma allora, programmazione orientata agli oggetti è molto più potente, una volta che si ottiene,.. E così è questa, uh ... quasi-orientata agli aspetti di programmazione, credo E una volta che capire metaclassi, sono in realtà molto semplice, quindi perché non usarli quando conviene?

E, infine, sono metaclassi rad, e la programmazione dovrebbe essere divertente. Utilizzando costrutti di programmazione standard e modelli di progettazione per tutto il tempo è noioso e poco interessante, e ostacola la vostra immaginazione. Vivi un po! Ecco un metametaclass, solo per te.

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

Lo scopo di metaclassi non è quello di sostituire la distinzione di classe / oggetto con metaclasse / classe - è di cambiare il comportamento di definizioni di classi (e quindi le loro istanze) in qualche modo. Effettivamente è di modificare il comportamento della dichiarazione di classe in modi che possono essere più utili per il tuo dominio particolare da quello predefinito. Le cose le ho utilizzate per sono:

  • sottoclassi di inseguimento, solitamente per registrare i gestori. Questo è utile quando si utilizza una configurazione stile plug-in, in cui si desidera registrare un gestore per una cosa particolare, semplicemente sottoclasse e la creazione di un paio di attributi di classe. per esempio. supponiamo che si scrive un gestore per vari formati musicali, dove ogni classe implementa metodi appropriati (Riproduzione / ottenere le etichette ecc) per il suo tipo. L'aggiunta di un gestore per un nuovo tipo diventa:

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here
    

    Il metaclasse mantiene quindi un dizionario di {'.mp3' : MP3File, ... } ecc, e costruisce un oggetto del tipo appropriato quando si richiede un gestore tramite una funzione di fabbrica.

  • Cambiare il comportamento. Si consiglia di allegare un significato speciale a certi attributi, con conseguente comportamento alterato quando sono presenti. Ad esempio, si potrebbe voler cercare metodi con il nome _get_foo e _set_foo e trasparente convertirli in proprietà. Come esempio del mondo reale, ecco una ricetta che ho scritto per dare più C-like definizioni struct . Il metaclasse viene utilizzato per convertire gli elementi dichiarati in una stringa di formato struct, la manipolazione di successione, ecc, e di produrre una classe in grado di trattare con esso.

    Per altri esempi reali, dare un'occhiata a vari ORM, come ORM del sqlalchemy o SQLObject . Anche in questo caso, lo scopo è quello di interpretare defintions (qui le definizioni delle colonne SQL) con un significato particolare.

Cominciamo citando classico di Tim Peter:

  

metaclassi sono più profonde magica del 99%   di utenti dovrebbe mai preoccuparsi. Se   ti chiedi se loro, è necessario   non (le persone che hanno effettivamente bisogno   loro sapere con certezza che   bisogno di loro, e non hanno bisogno di un   spiegazione sul perché). Tim Peters   (C.l.p inviare 2002/12/22)

Detto questo, ho (periodicamente) imbattersi in veri e propri usi di metaclassi. Quello che viene in mente è in Django in cui tutti i modelli ereditano da models.Model. models.Model, a sua volta, fa alcune gravi magia per avvolgere i vostri modelli DB con la bontà ORM di Django. Quella magia avviene per mezzo di metaclassi. Si crea ogni sorta di classi di eccezioni, le classi manager, ecc, ecc.

Vedere Django / db / modelli / base.py, classe ModelBase () per l'inizio della storia.

Le metaclassi possono essere utili per la costruzione di linguaggi specifici del dominio in Python.Esempi concreti sono Django, la sintassi dichiarativa degli schemi di database di SQLObject.

Un esempio di base da Una metaclasse conservatrice di Ian Bicking:

I metaclassi che ho usato sono stati principalmente per supportare una sorta di stile dichiarativo di programmazione.Per istanza, si consideri una convalida schema:

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

Alcune altre tecniche: Ingredienti per costruire un DSL in Python (PDF).

Modifica (di Ali):Un esempio di come farlo utilizzando raccolte e istanze è ciò che preferirei.Il fatto importante sono le istanze, che ti danno più potere ed eliminano il motivo per utilizzare le metaclassi.Vale inoltre la pena notare che il tuo esempio utilizza una combinazione di classi e istanze, il che è sicuramente un'indicazione che non puoi fare tutto semplicemente con le metaclassi.E crea un modo veramente non uniforme di farlo.

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

Non è perfetto, ma già la magia è quasi pari a zero, non sono necessarie metaclassi e l'uniformità è migliorata.

Un modello ragionevole di uso meta-classe sta facendo qualcosa di una volta quando una classe è definita piuttosto che ripetutamente ogni volta che la stessa classe viene creata un'istanza.

Quando più classi condividono lo stesso comportamento speciale, ripetendo __metaclass__=X è ovviamente meglio di ripetere il codice veicolo e / o introdurre superclassi ad hoc condivisa.

Ma anche con una sola classe speciale e senza estensione prevedibile, __new__ e __init__ di una meta-classe sono un modo più pulito per inizializzare le variabili di classe o altri dati globali di combinazione di unità di codice per usi speciali e normali istruzioni def e class nel corpo definizione della classe .

L'unica volta che ho usato metaclassi in Python è stato quando si scrive un wrapper per le API di Flickr.

Il mio obiettivo era quello di raschiare di flickr sito api e generare dinamicamente una gerarchia di classi completa per consentire l'accesso API utilizzando oggetti Python:

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

Quindi, in questo esempio, perché ho generato l'intera API di Python Flickr dal sito, io davvero non so le definizioni di classe in fase di esecuzione. Essere in grado di generare dinamicamente i tipi è stato molto utile.

Stavo pensando la stessa cosa solo ieri e completamente d'accordo. Le complicazioni nel codice causati da tentativi di renderlo più dichiarativo in generale rendere la base di codice più difficile da mantenere, più difficile da leggere e meno divinatorio a mio parere. Inoltre normalmente richiede un sacco di copy.copy () ING (per mantenere eredità e per copiare da classe a esempio) e significa che si deve guardare in molti luoghi per vedere cosa sta succedendo (sempre guardando da metaclasse up), che va contro la python grano anche. Ho raccogliendo attraverso FormEncode e sqlalchemy codice per vedere se uno stile così dichiarativa è valsa la pena e la sua chiaramente non. Tale stile dovrebbe essere lasciata ai descrittori (come la proprietà e metodi) e dei dati immutabili. Ruby ha un migliore supporto per tali stili dichiarativi e sono contento del linguaggio Python nucleo non sta andando questa strada.

Posso vedere il loro uso per il debug, aggiungere un metaclasse per tutte le classi di base per ottenere più ricchi informazioni. Vedo anche il loro uso solo in progetti di grandi dimensioni (molto) per sbarazzarsi di qualche codice standard (a scapito della chiarezza). SQLAlchemy per fa li usano altrove , per aggiungere un particolare metodo personalizzato per tutte le sottoclassi sulla base di un valore di attributo nella loro definizione di classe per esempio un esempio giocattolo

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

potrebbe avere un metaclasse che ha generato un metodo in quella classe con proprietà speciali basati su "ciao" (ad esempio un metodo che aggiunge "ciao" al fine di una stringa). Potrebbe essere buono per la manutenzione per assicurarsi che non c'era bisogno di scrivere un metodo in ogni sottoclasse si effettua invece tutto ciò che dovete definire è method_maker_value.

La necessità di questo è così raro e anche se taglia solo su un po 'di battitura che non è davvero la pena di considerare se non si ha una grande base di codice abbastanza.

Non si può mai assolutamente necessità di usare un metaclasse, perché si può sempre costruire una classe che fa quello che si desidera utilizzare l'ereditarietà o aggregazione della classe che si desidera modificare.

Detto questo, può essere molto utile in Smalltalk e Ruby per essere in grado di modificare una classe esistente, ma Python non piace farlo direttamente.

C'è un ottimo DeveloperWorks articolo su metaclassing in Python che potrebbe aiutare. Il rel="nofollow Wikipedia articolo è anche abbastanza buono.

metaclassi non stanno sostituendo la programmazione! Sono solo un trucco che può automatizzare o rendere più elegante alcuni compiti. Un buon esempio di questo è Pygments sintassi biblioteca evidenziazione. Ha una chiamata RegexLexer classe che consente all'utente di definire un insieme di regole Lexing come espressioni regolari su una classe. Una metaclasse è usato per girare le definizioni in un parser utile.

Sono come il sale; è facile da usare troppo.

Il modo che ho usato metaclassi era quello di fornire alcuni attributi alle classi. Prendiamo ad esempio:

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

inserirà l'attributo nome su ogni classe che avrà la metaclasse impostato per puntare a NameClass.

Alcune librerie GUI hanno problemi quando più thread tentano di interagire con loro. tkinter è un esempio; e mentre si può gestire in modo esplicito il problema con gli eventi e le code, può essere molto più semplice utilizzare la libreria in modo che ignora completamente il problema. Ecco -. La magia di metaclassi

Essere in grado di riscrivere in modo dinamico un'intera biblioteca senza soluzione di continuità in modo che funzioni correttamente come previsto in un'applicazione multithreading può essere estremamente utile in alcune circostanze. Il safetkinter modulo fa che, con l'aiuto di un meta-classe fornita dal threadbox modulo - gli eventi e le code non sono necessarie

Un aspetto pulito di threadbox è che non importa quale classe cloni. Esso fornisce un esempio di come tutte le classi di base possono essere toccati da un metaclasse, se necessario. Un ulteriore vantaggio che viene fornito con metaclassi è che corrono sulle classi che ereditano pure. I programmi che si scrivono - perché no?

L'unica legittima caso d'uso di un metaclasse è quello di mantenere altri sviluppatori curiosi di toccare il codice. Una volta che un ficcanaso maestri sviluppatore metaclassi e comincia a frugare con la vostra, gettare in un altro livello o due per tenerli fuori. Se questo non funziona, iniziare a utilizzare type.__new__ o forse qualche schema utilizzando un metaclasse ricorsivo.

(lingua scritta in guancia, ma ho visto questo tipo di offuscamento fatto. Django è un perfetto esempio)

Questo è un uso minore, ma ... una cosa che ho trovato utile per è quello di richiamare una funzione ogni volta che viene creata una sottoclasse metaclassi. Ho codificato questo in un metaclasse che sembra per un attributo __initsubclass__: ogni volta che viene creata una sottoclasse, tutte le classi genitore che definiscono tale metodo vengono invocati con __initsubclass__(cls, subcls). Questo permette la creazione di una classe genitore che poi registra tutte le sottoclassi con qualche registro globale, corre controlli invarianti su sottoclassi ogni volta che vengono definiti, effettuare le operazioni di late-binding, ecc ... il tutto senza bisogno di richiamare manualmente le funzioni di o per creare metaclassi personalizzate che eseguono ognuna di queste mansioni separate.

Intendiamoci, ho lentamente arriva a capire la magicalness implicita di questo comportamento è in qualche modo indesiderato, dal momento che è inaspettato se guardando una definizione di classe fuori dal contesto ... e così ho spostato lontano da utilizzando tale soluzione per nulla di grave oltre l'inizializzazione di un attributo __super per ogni classe e l'istanza.

Recentemente ho dovuto usare un metaclasse per aiutare in modo dichiarativo definire un modello SQLAlchemy intorno a un tavolo di database popolato con i dati del censimento degli Stati Uniti da http://census.ire.org/data/bulkdata.html

gusci di database per le tabelle di dati censimento, che creano colonne interi secondo una convenzione di denominazione dal Bureau censimento di p012015, p012016, p012017, ecc.

Ho voluto a) essere in grado di accedere a queste colonne utilizzando una sintassi model_instance.p012017, b) essere abbastanza esplicito su quello che stavo facendo e c) non c'è bisogno di definire in modo esplicito decine di campi sul modello, quindi sottoclasse DeclarativeMeta di SQLAlchemy a scorrere una serie di colonne e creare automaticamente i campi modello corrispondenti alle colonne:

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

ho potuto quindi utilizzare questo metaclasse per la mia definizione del modello e accedere ai campi enumerati automaticamente sul modello:

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...

Sembra che ci sia un uso legittimo descritto qui - riscrittura di Python docstrings con metaclasse

.

Ho dovuto usare una volta per un parser binario per renderlo più facile da usare. Si definisce una classe di messaggio con gli attributi dei campi presenti sul filo. Avevano bisogno di essere ordinati in modo sono state dichiarate per costruire il filo formato finale da esso. È possibile farlo con metaclassi, se si utilizza un dict namespace ordinato. Infatti, il suo negli esempi per metaclassi:

https://docs.python.org/3/reference/ datamodel.html # metaclasse-esempio

Ma in generale:. Molto attentamente valutare, se davvero ha realmente bisogno la complessità aggiunto di metaclassi

la risposta da @ Dan Gittik è cool

Gli esempi alla fine potrebbero chiarire molte cose, ho cambiato in Python 3 e dare qualche spiegazione:

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls

        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

#China is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it's __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it's __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan

  • tutto è oggetto, in modo classe è oggetto
  • oggetto di classe è stato creato da metaclasse
  • tutte le classi inheritted dal tipo è metaclasse
  • metaclasse poteva controllare classe creando
  • metaclasse metaclasse poteva controllare la creazione di troppo (in modo che possa ciclo per sempre)
  • questa è la metaprogrammazione ... si poteva controllare il sistema di tipi in fase di esecuzione
  • di nuovo, tutto è oggetto, questo è un sistema uniforme, digitare creare il tipo, e digitare creare l'istanza

Un altro caso d'uso è quando si vuole essere in grado di modificare gli attributi a livello di classe ed essere sicuri che colpisce solo l'oggetto a portata di mano. In pratica, questo implica "fondere" le fasi di metaclassi e classi istanze, quindi si porta a trattare solo con istanze di classe della loro stessa specie (unico).

ho avuto anche a che fare che, quando (per le preoccupazioni di readibility polimorfismo ) abbiamo voluto definire dinamicamente property s che restituito valori (eventualmente) derivare da calcoli basati su ( cambiando spesso) gli attributi a livello di istanza, che può essere fatto solo a livello di classe , es , dopo la creazione di istanze metaclasse e prima che l'istanza di classe.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top