Quali sono alcuni casi d'uso (concreti) per le metaclassi?
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)
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:
-
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 dizionariomodels
:>>> 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 :(
-
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 attributiField
, 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.