Ce bout de code vous permet de créer de nouvelles classes avec des noms et des noms de paramètres dynamiques. et des noms de paramètres dynamiques. La vérification des paramètres dans __init__
ne permet pas paramètres inconnus, si vous avez besoin d'autres vérifications, telles que type, ou qu'ils sont obligatoires, il suffit d'ajouter la logique ici :
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
Et ça marche comme ça, par exemple :
>>> 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
Je vois que vous demandez l'insertion des noms dynamiques dans la portée de nommage -- maintenant, que n'est pas considéré comme une bonne pratique en Python - vous avez soit soit des noms de variables, connus au moment du codage, soit des données - et les noms appris en cours d'exécution sont plus des "données" que des "variables" -
Ainsi, vous pourriez simplement ajouter vos classes à un dictionnaire et les utiliser à partir de là :
name = "SpecialClass"
classes = {}
classes[name] = ClassFactory(name, params)
instance = classes[name](...)
Et si votre conception a absolument besoin que les noms entrent dans la portée, faites simplement la même chose, mais utilisez le dictionnaire renvoyé par la fonction globals()
au lieu d'un dictionnaire arbitraire :
name = "SpecialClass"
globals()[name] = ClassFactory(name, params)
instance = SpecialClass(...)
(Il serait en effet possible pour la fonction de fabrique de classe d'insérer le nom dynamiquement dans la portée globale de l'appelant - mais c'est une pratique encore pire, et ce n'est pas compatible avec les implémentations Python. La façon de le faire serait d'obtenir le cadre d'exécution de l'appelant, par l'intermédiaire de sys._getframe(1) et en définissant le nom de la classe dans le dictionnaire global du cadre dans sa section f_globals
).
mise à jour, tl;dr : Cette réponse est devenue populaire, mais elle est très spécifique au corps de la question. La réponse générale sur la façon de "créer dynamiquement des classes dérivées à partir d'une classe de base" en Python est un simple appel à type
en transmettant le nom de la nouvelle classe, un tuple avec la ou les classes de base et le nom de la classe de base. __dict__
corps pour la nouvelle classe - comme ceci :
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
mise à jour
Les personnes qui en ont besoin doivent également vérifier le aneth Il prétend être capable d'extraire et d'enlever les classes comme le fait Pickle pour les objets ordinaires, et il l'a fait dans certains de mes tests.