¿Cómo puedo crear dinámicamente clases derivadas de una clase base?

Por ejemplo tengo una clase base de la siguiente manera:

class BaseClass(object): def __init__(self, classtype): self._type = classtype 

De esta clase obtengo varias otras clases, por ejemplo

 class TestClass(BaseClass): def __init__(self): super(TestClass, self).__init__('Test') class SpecialClass(BaseClass): def __init__(self): super(TestClass, self).__init__('Special') 

¿Existe una forma agradable y pirónica de crear esas clases dinámicamente mediante una llamada a la función que ponga la nueva clase en mi ámbito actual, como:

 foo(BaseClass, "My") a = MyClass() ... 

Como habrá comentarios y preguntas sobre por qué necesito esto: todas las clases derivadas tienen exactamente la misma estructura interna con la diferencia de que el constructor toma una cantidad de argumentos previamente no definidos. Entonces, por ejemplo, MyClass toma las palabras clave por a tiempo, mientras que el constructor de la clase TestClass toma c .

 inst1 = MyClass(a=4) inst2 = MyClass(a=5) inst3 = TestClass(b=False, c = "test") 

Pero NUNCA deben usar el tipo de la clase como argumento de entrada como

 inst1 = BaseClass(classtype = "My", a=4) 

Conseguí que esto funcionara, pero preferiría lo contrario, es decir, objetos de clase creados dinámicamente.

Este bit de código le permite crear nuevas clases con nombres dynamics y nombres de parámetros. La verificación de parámetros en __init__ simplemente no permite parámetros desconocidos, si necesita otras verificaciones, como el tipo, o que son obligatorias, simplemente agregue la lógica allí:

 class BaseClass(object): def __init__(self, classtype): self._type = classtype def ClassFactory(name, argnames, BaseClass=BaseClass): def __init__(self, **kwargs): for key, value in kwargs.items(): # here, the argnames variable is the one passed to the # ClassFactory call if key not in argnames: raise TypeError("Argument %s not valid for %s" % (key, self.__class__.__name__)) setattr(self, key, value) BaseClass.__init__(self, name[:-len("Class")]) newclass = type(name, (BaseClass,),{"__init__": __init__}) return newclass 

Y esto funciona así, por ejemplo:

 >>> SpecialClass = ClassFactory("SpecialClass", "abc".split()) >>> s = SpecialClass(a=2) >>> sa 2 >>> s2 = SpecialClass(d=3) Traceback (most recent call last): File "", line 1, in  File "", line 8, in __init__ TypeError: Argument d not valid for SpecialClass 

Veo que está solicitando la inserción de los nombres dynamics en el ámbito de los nombres, ahora no se considera una buena práctica en Python, ya sea que tenga nombres de variables, conocidos en el momento de la encoding, o datos, y los nombres aprendidos en tiempo de ejecución son más ” datos “que” variables “-

Entonces, puedes simplemente agregar tus clases a un diccionario y usarlas desde allí:

 name = "SpecialClass" classes = {} classes[name] = ClassFactory(name, params) instance = classes[name](...) 

Y si su diseño necesita absolutamente los nombres para entrar en el scope, haga lo mismo, pero use el diccionario devuelto por la llamada globals() lugar de un diccionario arbitrario:

 name = "SpecialClass" globals()[name] = ClassFactory(name, params) instance = SpecialClass(...) 

(De hecho, sería posible que la función de fábrica de clase inserte dinámicamente el nombre en el scope global de la persona que llama, pero esa es una práctica aún peor, y no es compatible en todas las implementaciones de Python. La forma de hacerlo sería obtener la información de la persona que llama. marco de ejecución, a través de sys._getframe (1) y estableciendo el nombre de la clase en el diccionario global del marco en su atributo f_globals ).

update, tl; dr: Esta respuesta se ha vuelto popular, aún así es muy específica para el cuerpo de la pregunta. La respuesta general sobre cómo “crear dinámicamente clases derivadas a partir de una clase base” en Python es una simple llamada al type pasa el nombre de la nueva clase, una tupla con la clase de base y el cuerpo __dict__ para la nueva clase, como esto:

 >>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...}) 

actualizar
Cualquiera que necesite esto también debería verificar el proyecto de eneldo : dice ser capaz de encurtir y desentrañar clases al igual que el encurtido hace con objetos comunes, y lo había aceptado en algunas de mis pruebas.

type() es la función que crea clases (y en particular subclases):

 def set_x(self, value): self.x = value SubClass = type('SubClass', (BaseClass,), {'set_x': set_x}) # (More methods can be put in SubClass, including __init__().) obj = SubClass() obj.set_x(42) print obj.x # Prints 42 print isinstance(obj, BaseClass) # True