2010-10-12 16 views
9

que estoy tratando de escribir una metaclase genérico para las subclases de seguimientoPython Genérico MetaClass para realizar un seguimiento de las subclases

Como yo quiero que esto sea genérica, que no quería a codificar cualquier nombre de clase dentro de este metaclase, por lo tanto, se me ocurrió una función que genera la metaclase adecuada, algo así como:

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 

Esta manera de que pudiera invocarlo para generar una metaclase para una raíz clase específica con:

__metaclass__ = make_subtracker(Root) 

Aquí es donde me encuentro con un problema. No puedo hacer esto:

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

... porque Root no está definido aún cuando uso make_subtracker(Root). He intentado añadir el atributo metaclasemás tarde, por lo que al menos se puede aplicar en las subclases:

class Root(object): 
    _registry = [] 

Root.__metaclass__ = make_subtracker(Root) 

... pero esto no funciona. metaclase tiene un procesamiento especial cuando se lee la definición de clase, como se define en http://docs.python.org/reference/datamodel.html#customizing-class-creation

Busco sugerencias con el fin de hacer esto (ya sea cambiar una metaclase clase en tiempo de ejecución de una manera que se aplica a sus subclases, o cualquier otra alternativa).

+1

Por favor, no hagas esto. La gente que viene después la arrancará porque es demasiado compleja. Utilice una función de fábrica que cree objetos de la subclase adecuada. –

Respuesta

8

Python lo hace automáticamente para las clases de nuevo estilo, como se ha mencionado en este answer a la queston similares How can I find all subclasses of a given class in Python? aquí.

+1

Aunque técnicamente se realiza un seguimiento, dependiendo de su uso, puede que no sea muy eficiente. Devuelve una lista, por lo que si está buscando una clase con atributos específicos, deberá iterar sobre toda la lista. El uso de una metaclase le permite indexar subcatas de manera eficiente como lo desee. – Cerin

+0

@Cerin: Tal vez ... si la alta eficiencia es una preocupación o problema, que no puede determinarse a partir de la pregunta que no especifica cómo se usará la información. De todos modos, me interesaría ver una implementación concreta de tu (s) idea (s), ya que todas las otras respuestas actualmente también están basadas en listas, así que no dudes en agregar una propia. – martineau

+0

@martinaeu, Mi implementación es la misma que la de aaronasterling, excepto que el registro es un diccionario en lugar de una lista, y la clave es la representación de hash que quieras usar para cada clase. Actualmente estoy usando esto como una alternativa al patrón register() en Django, para asociar una clase ModelForm con un slug único, para el envío rápido de URL. – Cerin

8

Creo que quieres algo como esto (no probado):

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) 

Entonces, para Python 2, se puede invocar como:

class Root(object): 
    __metaclass__ = SubclassTracker 

para Python 3

class Root(object, metaclass=SubclassTracker): 

Tenga en cuenta que no necesita pegar el atributo _registry allí porque cosas así son las metaclas ses son para. Puesto que ya tiene uno tendido alrededor ...;)

Tenga en cuenta también que es posible que desee mover el código de registro en una cláusula else para que la clase no se registre como una subclase.

+0

Hay un error de sintaxis en el tipo (metaclass.root._registry.append (cls) ... Lo cambié a: 'cls._registry.append (cls)' que funciona –

+0

@Carles Ese fue un error de sintaxis fea. Como basura completa y completa. Edité algunas veces, por lo que fueron algunas ideas diferentes para algunas líneas diferentes que deberían haberse eliminado. Lo siento. Se ve bien ahora. Gracias por señalarlo. – aaronasterling

+0

De hecho, dado que SubclassTracker no incluye ninguna referencia codificada al * tipo * raíz, no hay necesidad de una función para crearlo, por lo tanto, definí directamente una clase SubclassTracker y olvidé por completo el make_subtracker. Funciona bien, gracias! –

1

Aquí es algo que he estado jugando con (que funciona):

Cuestiones relacionadas