2010-10-12 11 views
9

sto cercando di scrivere un metaclasse generico per il monitoraggio sottoclassiPython generico metaclasse per tenere traccia delle sottoclassi

Dal momento che voglio che questo sia generica, non ho voglia di codificare qualsiasi nome di classe all'interno di questa metaclasse, quindi, mi si avvicinò con una funzione che genera il corretto metaclasse, qualcosa di simile:

def make_subtracker(root): 
    class SubclassTracker(type): 
     def __init__(cls, name, bases, dct): 
      print('registering %s' % (name,)) 
      root._registry.append(cls) 
      super(SubclassTracker, cls).__init__(name, bases, dct) 
    return SubclassTracker 

questo modo ho potuto invocare per generare un metaclasse per una specifica radice classe con:

__metaclass__ = make_subtracker(Root) 

Ecco dove mi imbatto in un problema. Non posso fare questo:

class Root(object): 
    _registry = [] 
    __metaclass__ = make_subtracker(Root) 

... perché Root non è ancora definita quando uso make_subtracker(Root). Ho provato ad aggiungere la metaclasse attributo successivamente, in modo che almeno può essere applicato in sottoclassi:

class Root(object): 
    _registry = [] 

Root.__metaclass__ = make_subtracker(Root) 

... ma questo non funziona. metaclasse ha un trattamento speciale quando la definizione della classe viene letta, come definito nella http://docs.python.org/reference/datamodel.html#customizing-class-creation

Sto cercando suggerimenti per fare questo (o cambiare un metaclasse di classe in fase di esecuzione in un modo che si applica a le sue sottoclassi o qualsiasi altra alternativa).

+1

Si prega di non farlo. La gente che viene dopo te la strapperà perché è troppo complessa. Utilizzare una funzione di fabbrica che crei oggetti della sottoclasse appropriata. –

risposta

8

Python fa automaticamente per classi di nuovo stile, come accennato in questo answer al queston simile How can I find all subclasses of a given class in Python? qui.

+1

Anche se tecnicamente tiene traccia, a seconda dell'utilizzo, potrebbe non essere molto efficiente. Restituisce una lista, quindi se stai cercando una classe con attributi specifici, dovrai scorrere l'intera lista. L'utilizzo di una meta classe ti consente di indicizzare in modo efficiente i sottoclassi come desideri. – Cerin

+0

@Cerin: Forse ... se l'alta efficienza è una preoccupazione o un problema - che non può essere determinato dalla domanda che non specifica come saranno utilizzate le informazioni. Indipendentemente da ciò, sarei interessato a vedere una implementazione concreta delle tue idee in quanto tutte le altre risposte sono attualmente anche basate su elenchi - quindi sentiti libero di aggiungerne una tua. – martineau

+0

@martinaeu, La mia implementazione è la stessa di aaronasterling, tranne che il registro è un dizionario invece di una lista e la chiave è la rappresentazione con l'hash che vorresti usare per ogni classe. Attualmente sto usando questo come alternativa al pattern register() in Django, per associare una classe ModelForm a una lumaca unica, per un dispacciamento rapido dell'URL. – Cerin

8

Penso che si desidera qualcosa di simile (non testata):

class SubclassTracker(type): 
    def __init__(cls, name, bases, dct): 
     if not hasattr(cls, '_registry'): 
      cls._registry = [] 
     print('registering %s' % (name,)) 
     cls._registry.append(cls) 
     super(SubclassTracker, cls).__init__(name, bases, dct) 

Poi, per Python 2, è possibile richiamare le cose come:

class Root(object): 
    __metaclass__ = SubclassTracker 

per Python 3

class Root(object, metaclass=SubclassTracker): 

Nota che non è necessario attaccare l'attributo _registry lì perché roba del genere è ciò che metaclas ses sono per. Dal momento che ne hai già uno in giro ...;)

Nota anche che potresti voler spostare il codice di registrazione in una clausola else in modo che la classe non si registri come sottoclasse.

+0

C'è un errore di sintassi nel tipo (metaclass.root._registry.append (cls) ... L'ho modificato in: 'cls._registry.append (cls)' che funziona –

+0

@Carles Questo è stato un brutto errore di sintassi. Come la spazzatura completa e totale, ho modificato alcune volte quindi c'erano alcune idee diverse per alcune linee diverse che avrebbero dovuto essere cancellate, mi dispiace, sembra buono ora Grazie per averlo fatto notare. – aaronasterling

+0

Infatti, dato che SubclassTracker non include alcun riferimento hardcoded al tipo * root *, non c'è bisogno di una funzione per crearlo, quindi ho definito direttamente una classe SubclassTracker e ho completamente dimenticato make_subtracker. Funziona bene, grazie! –

1

Ecco qualcosa che ho giocato in giro con (che funziona):

Problemi correlati