Par exemple, j'ai une classe de base comme suit:
class BaseClass(object):
def __init__(self, classtype):
self._type = classtype
De cette classe je dérive plusieurs autres classes, par exemple
class TestClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Test')
class SpecialClass(BaseClass):
def __init__(self):
super(TestClass, self).__init__('Special')
Existe-t-il une manière pythonique agréable de créer ces classes dynamiquement par un appel de fonction qui place la nouvelle classe dans ma portée actuelle, comme:
foo(BaseClass, "My")
a = MyClass()
...
Comme il y aura des commentaires et des questions pourquoi j'ai besoin de ceci: Les classes dérivées ont toutes exactement la même structure interne avec la différence que le constructeur prend un certain nombre d'arguments précédemment non définis. Ainsi, par exemple, MyClass
prend les mots-clés a
tandis que le constructeur de la classe TestClass
prend b
et c
.
inst1 = MyClass(a=4)
inst2 = MyClass(a=5)
inst3 = TestClass(b=False, c = "test")
Mais ils ne doivent JAMAIS utiliser le type de la classe comme argument d'entrée comme
inst1 = BaseClass(classtype = "My", a=4)
J'ai réussi à faire fonctionner cela mais je préférerais l'inverse, c'est-à-dire des objets de classe créés dynamiquement.
la source
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)
?Réponses:
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:Et cela fonctionne comme ceci, par exemple:
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à:
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:(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: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.
la source
BaseClass.__init__()
serait mieux comme le plus généralsuper(self.__class__).__init__()
, qui joue plus bien lorsque les nouvelles classes sont sous-classées. (Référence: rhettinger.wordpress.com/2011/05/26/super-consemed-super )super
ci-dessus et créez une sous-classe d'une classe créée dynamiquement pour la comprendre; Et, d'un autre côté, dans ce cas, vous pouvez avoir la classe de base comme objet général à partir duquel appeler__init__
.__init__
deBaseClass
est appelé avec un argument, mais en faitBaseClass.__init__
prend toujours une liste arbitraire d'arguments de mots-clés. Deuxièmement, la solution ci-dessus définit tous les noms de paramètres autorisés en tant qu'attributs, ce qui n'est pas ce que je souhaite. TOUT argument DOIT allerBaseClass
, mais celui que je connais lors de la création de la classe dérivée. Je vais probablement mettre à jour la question ou en poser une plus précise pour la clarifier.super()
donne que je mentionnaisTypeError: must be type, not SubSubClass
. Si je comprends bien, cela vient du premier argumentself
de__init__()
, qui est unSubSubClass
où untype
objet est attendu: cela semble lié au faitsuper(self.__class__)
est un super objet non lié. Quelle est sa__init__()
méthode? Je ne sais pas quelle méthode pourrait nécessiter un premier argument de typetype
. Pourriez-vous expliquer? (Note latérale: monsuper()
approche n'a en effet pas de sens, ici, car__init__()
a une signature variable.)type()
est la fonction qui crée des classes (et en particulier des sous-classes):la source
TypeError
qui dit__init__() takes exactly 2 arguments (1 given)
. J'ai trouvé que l'ajout de quelque chose (quelque chose?) Pour combler le vide suffirait. Par exemple,obj = SubClass('foo')
s'exécute sans erreur.SubClass
s'agit d'une sous-classe deBaseClass
dans la question etBaseClass
prend un paramètre (classtype
, qui est'foo'
dans votre exemple).Pour créer une classe avec une valeur d'attribut dynamique, consultez le code ci-dessous. NB. Ce sont des extraits de code en langage de programmation Python
la source