Ce bit de code vous permet de créer de nouvelles classes avec des noms dynamiques et des noms de paramètres. La vérification des paramètres dans __init__
n'autorise tout simplement pas les paramètres inconnus, si vous avez besoin d'autres vérifications, comme le type, ou qu'elles sont obligatoires, ajoutez simplement 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 cela fonctionne comme ceci, 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 d'insérer les noms dynamiques dans la portée de dénomination - maintenant, cela n'est pas considéré comme une bonne pratique en Python - vous avez soit des noms de variables, connus au moment du codage, soit des données - et les noms appris au moment de l'exécution le sont davantage " data "que" variables "-
Ainsi, vous pouvez 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 de même, mais utilisez le dictionnaire renvoyé par l' globals()
appel 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 classes d'insérer le nom dynamiquement sur la portée globale de l'appelant - mais c'est une pratique encore pire, et n'est pas compatible entre les implémentations Python. La façon de faire serait d'obtenir l'appelant frame d'exécution, via sys._getframe (1) et en définissant le nom de la classe dans le dictionnaire global du frame dans son f_globals
attribut).
update, tl; dr: Cette réponse était devenue populaire, toujours 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 pour type
passer le nouveau nom de classe, un tuple avec la ou les classes de base et le __dict__
corps de la nouvelle classe - comme ceci:
>>> new_class = type("NewClassName", (BaseClass,), {"new_method": lambda self: ...})
update
Quiconque a besoin de cela devrait également vérifier le projet d' aneth - il prétend être capable de décaper et de décoller des classes comme le fait pickle pour des objets ordinaires, et y avait vécu dans certains de mes tests.
a
ce sera toujoursMyClass
etTestClass
ne prendra jamais una
? Pourquoi ne pas simplement déclarer les 3 argumentsBaseClass.__init__()
mais les utiliser par défautNone
?def __init__(self, a=None, b=None, C=None)
?