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í:
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 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í:
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
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:
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(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:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
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.
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)
?