una otra objeto llamar listas instanciar instancia ejemplo dentro crear con como clases clase python class inheritance

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



llamar una clase dentro de otra clase python (2)

Por ejemplo, tengo una clase base de la siguiente manera:

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

De esta clase derivo 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 pitónica de crear esas clases dinámicamente mediante una llamada a función que pone 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 indefinidos. Entonces, por ejemplo, MyClass toma las palabras clave mientras 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)

Lo hice funcionar pero preferiría el otro modo, es decir, objetos de clase creados dinámicamente.


Este pequeño código le permite crear nuevas clases con nombres dinámicos y nombres de parámetros. La verificación de parámetros en __init__ simplemente no permite parámetros desconocidos, si necesita otras verificaciones, como 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", "a b c".split()) >>> s = SpecialClass(a=2) >>> s.a 2 >>> s2 = SpecialClass(d=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 8, in __init__ TypeError: Argument d not valid for SpecialClass

Veo que está pidiendo insertar los nombres dinámicos en el ámbito de nombres; ahora, eso no se considera una buena práctica en Python; tiene nombres de variables, conocidos en tiempo de codificación, o datos; y los nombres aprendidos en tiempo de ejecución son más ". datos "que" variables "-

Entonces, podrías 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 alcance, simplemente haga lo mismo, pero utilice 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 la clase inserte el nombre de forma dinámica en el alcance global de la persona que llama, pero esa es una práctica aún peor, y no es compatible con las implementaciones de Python. La forma de hacerlo sería obtener el llamador marco de ejecución, a través de sys._getframe(1) y estableciendo el nombre de clase en el diccionario global del marco en su atributo f_globals ).

update, tl; dr: Esta respuesta se hizo popular, todavía es muy específica para el cuerpo de la pregunta. La respuesta general sobre cómo "crear dinámicamente clases derivadas de una clase base" en Python es una simple llamada a type pasando el nuevo nombre de clase, una tupla con la clase base (es) y el cuerpo __dict__ para la nueva clase, como este:

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

actualizar
Cualquier persona que necesite esto también debería verificar el proyecto de dill : afirma ser capaz de extraer y desmantelar las clases como lo hace Pickle en objetos ordinarios, y lo ha vivido 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