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, p. Ej.
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 de función que coloca la nueva clase en mi alcance actual, como:
foo(BaseClass, "My")
a = MyClass()
...
Como habrá comentarios y preguntas, ¿por qué necesito esto? Todas las clases derivadas tienen exactamente la misma estructura interna con la diferencia de que el constructor toma una serie de argumentos previamente no definidos. Entonces, por ejemplo, MyClass
toma las palabras clave a
mientras que el constructor de la clase TestClass
toma b
y 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.
fuente
a
, siempre seráMyClass
yTestClass
nunca tomaré una
? ¿Por qué no simplemente declarar los 3 argumentos enBaseClass.__init__()
pero predeterminarlos todosNone
?def __init__(self, a=None, b=None, C=None)
?Respuestas:
Este bit de 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 el tipo, o si son obligatorias, simplemente agregue la lógica allí:Y esto funciona así, por ejemplo:
Veo que está pidiendo insertar los nombres dinámicos en el ámbito de denominación; ahora, eso no se considera una buena práctica en Python; tiene nombres de variables, conocidos en el momento de la codificación o datos, y los nombres aprendidos en tiempo de ejecución son más " datos "que" variables "-
Por lo tanto, puede agregar sus clases a un diccionario y usarlas desde allí:
Y si su diseño necesita absolutamente que los nombres entren en el alcance, simplemente haga lo mismo, pero use el diccionario devuelto por la
globals()
llamada en lugar de un diccionario arbitrario:(De hecho, sería posible que la función de fábrica de clases insertara el nombre dinámicamente en el alcance global de la persona que llama, pero esa es una práctica aún peor y no es compatible con todas las implementaciones de Python. La forma de hacerlo sería obtener el nombre 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
f_globals
atributo).update, tl; dr: esta respuesta se ha vuelto popular, aún 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 a
type
pasar el nombre de la nueva clase, una tupla con la (s) clase (s) base (s) y el__dict__
cuerpo de la nueva clase, como esta:actualización
Cualquiera que necesite esto también debería verificar el proyecto eneldo : afirma ser capaz de encurtir y desacoplar clases tal como lo hace pickle con objetos ordinarios, y lo había cumplido en algunas de mis pruebas.
fuente
BaseClass.__init__()
sería mejor como el más generalsuper(self.__class__).__init__()
, que funciona mejor cuando las nuevas clases se subclasifican. (Referencia: rhettinger.wordpress.com/2011/05/26/super-considered-super )super
anterior y cree una subclase de una clase creada dinámicamente para comprenderlo; Y, por otro lado, en este caso puede tener la clase base como un objeto general desde el cual llamar__init__
.__init__
ofBaseClass
con un argumento, pero de hechoBaseClass.__init__
siempre toma una lista arbitraria de argumentos de palabras clave. En segundo lugar, la solución anterior establece todos los nombres de parámetros permitidos como atributos, que no es lo que quiero. CUALQUIER argumento TIENE que irBaseClass
, pero cuál sé al crear la clase derivada. Probablemente actualizaré la pregunta o haré una más precisa para aclararla.super()
que estaba mencionando daTypeError: must be type, not SubSubClass
. Si lo entiendo correctamente, esto proviene del primer argumentoself
de__init__()
, que es un lugarSubSubClass
dondetype
se espera un objeto: esto parece estar relacionado con el hecho de quesuper(self.__class__)
es un superobjeto no vinculado . Cual es su__init__()
metodo? No estoy seguro de qué método de este tipo podría requerir un primer argumento de tipotype
. ¿Podrías explicar? (Nota al margen: misuper()
enfoque de hecho no tiene sentido, aquí, porque__init__()
tiene una firma variable.)type()
es la función que crea clases (y en particular subclases):fuente
TypeError
que decía__init__() takes exactly 2 arguments (1 given)
. Descubrí que agregar algo (¿algo?) Para llenar el vacío sería suficiente. Por ejemplo, seobj = SubClass('foo')
ejecuta sin errores.SubClass
es una subclase deBaseClass
en la pregunta yBaseClass
toma un parámetro (classtype
, que está'foo'
en su ejemplo).Para crear una clase con un valor de atributo dinámico, consulte el siguiente código. NÓTESE BIEN. Estos son fragmentos de código en el lenguaje de programación Python
fuente