2010-03-17 12 views
11

Quiero añadir atttributes clase a una superclase de forma dinámica. Por otra parte, quiero crear clases que heredan de esta superclase de forma dinámica, y el nombre de las subclases debe depender de la entrada del usuario.Python: generador de clases usando la entrada del usuario como nombres de clase

hay una "Unidad" superclase, a la cual puedo añadir atributos en tiempo de ejecución. Esto ya funciona

def add_attr (cls, name, value): 
    setattr(cls, name, value) 

class Unit(object): 
    pass 

class Archer(Unit): 
    pass 

myArcher = Archer() 
add_attr(Unit, 'strength', 5) 
print "Strenght ofmyarcher: " + str(myArcher.strength) 
Unit.strength = 2 
print "Strenght ofmyarcher: " + str(myArcher.strength) 

Esto conduce a la salida deseada:
Fuerza ofmyarcher: 5
Fuerza ofmyarcher: 2

Pero ahora no quiero predefinir la subclase Archer, pero prefiero dejar que la el usuario decide cómo llamar a esta subclase. He intentado algo como esto:

class Meta(type, subclassname): 
    def __new__(cls, subclassname, bases, dct): 
    return type.__new__(cls, subclassname, Unit, dct) 

factory = Meta()  
factory.__new__("Soldier") 

pero no hubo suerte. Creo que realmente no han entendido lo que nueva hace aquí. Lo que quiero como resultado aquí es

class Soldier(Unit): 
    pass 

siendo creado por la fábrica. Y si llamo a la fábrica con el argumento de "Caballero", me gustaría un caballero de clase, subclase de Unidad, que se creará.

¿Alguna idea? ¡Muchas gracias de antemano!
adiós
-Sano

Respuesta

14

para crear una clase de un nombre, utilice la instrucción class y asignar el nombre. Observe:

def meta(name): 
    class cls(Unit): 
     pass 

    cls.__name__ = name 
    return cls 

Ahora supongo que debo explicarme, y así sucesivamente. Cuando se crea una clase con la sentencia de clase, se hace dynamically-- es equivalente a llamar type().

Por ejemplo, las siguientes dos fragmentos hacen lo mismo:

class X(object): pass 
X = type("X", (object,), {}) 

El nombre de una class-- el primer argumento de type-- se asigna a __name__, y eso es básicamente el final de ese (la única vez __name__ se utiliza en sí es, probablemente, en la implementación predeterminada __repr__()). Para crear una clase con un nombre dinámico, puede, de hecho, el tipo de llamada como tal, o puede simplemente cambiar el nombre de la clase después. existe la sintaxis class por una razón, aunque-- es conveniente, y es fácil de añadir y cambiar las cosas después.Si desea agregar métodos, por ejemplo, sería

class X(object): 
    def foo(self): print "foo" 

def foo(self): print "foo" 
X = type("X", (object,), {'foo':foo}) 

y así sucesivamente. Así que aconsejaría usar la declaración de clase: si hubieras sabido que podrías hacerlo desde el principio, probablemente lo hubieras hecho. Tratar con type y demás es un desastre.

(No debe, por cierto, llame type.__new__() a mano, solamente type())

+0

Gracias Devin! Tanto su solución como la de Félix parecen tener un efecto secundario que no he considerado: la nueva clase es un objeto y debe engancharse a algo, almacenada en un varibale o añadida a una lista o algo. No puedo simplemente crear la clase Knight y luego llamar al constructor con myKnight = Knight(). Por el contrario, tengo que hacer algo como esto: unitlist.append (meta ("Knight")) myKnight = unitlist [0]() ¿Hay alguna manera de hacerlo? Para decirlo sin rodeos: Guarde la clase Knight en la misma "posición general" en la que se guarda la clase de la Unidad cuando se alcanza su definición en el código. – Sano98

+0

@ Sano98: Realmente no estoy seguro de lo que quieres decir. Puedes hacer 'Knight = meta (" Knight ")' y luego hacer 'myKnight = Knight()'. ¿Qué quieres exactamente? –

+0

¡Gracias, eso es exactamente lo que quiero hacer! Simplemente guárdalo debajo de Knight ... Y luego está en el espacio de nombres global y puedo usar la clase como si estuviera codificada. Genial, gracias. Simplemente no pensé que podría llamar a Knight() al guardar la clase como Knight, pero claro, ¿por qué no? – Sano98

7

Tener un vistazo a la función incorporada type().

knight_class = type('Knight', (Unit,), {}) 
  • Primer parámetro: Nombre de la nueva clase
  • segundo parámetro: tupla de las clases padre
  • tercer parámetro: el diccionario de atributos de clase.

Pero en su caso, si las subclases no implementan un comportamiento diferente, tal vez dando la clase Unit un atributo name es suficiente.

+0

Gracias, Félix, lo que sugiere funciona tan bien! – Sano98

+0

Encontré tu respuesta muy útil. ¡Gracias por agregarlo a pesar de que los otros ya están allí! – Profane

Cuestiones relacionadas