J'ai une classe qui m'est fournie par une bibliothèque externe. J'ai créé une sous-classe de cette classe. J'ai aussi une instance de la classe d'origine.
Je souhaite maintenant tourner cette instance dans une instance de mon sous-classe sans modifier les propriétés que l'instance a déjà (à l'exception de ceux que mon sous-classe remplace de toute façon).
La solution suivante qui semble fonctionner.
# This class comes from an external library. I don't (want) to control
# it, and I want to be open to changes that get made to the class
# by the library provider.
class Programmer(object):
def __init__(self,name):
self._name = name
def greet(self):
print "Hi, my name is %s." % self._name
def hard_work(self):
print "The garbage collector will take care of everything."
# This is my subclass.
class C_Programmer(Programmer):
def __init__(self, *args, **kwargs):
super(C_Programmer,self).__init__(*args, **kwargs)
self.learn_C()
def learn_C(self):
self._knowledge = ["malloc","free","pointer arithmetic","curly braces"]
def hard_work(self):
print "I'll have to remember " + " and ".join(self._knowledge) + "."
# The questionable thing: Reclassing a programmer.
@classmethod
def teach_C(cls, programmer):
programmer.__class__ = cls # <-- do I really want to do this?
programmer.learn_C()
joel = C_Programmer("Joel")
joel.greet()
joel.hard_work()
#>Hi, my name is Joel.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
jeff = Programmer("Jeff")
# We (or someone else) makes changes to the instance. The reclassing shouldn't
# overwrite these.
jeff._name = "Jeff A"
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>The garbage collector will take care of everything.
# Let magic happen.
C_Programmer.teach_C(jeff)
jeff.greet()
jeff.hard_work()
#>Hi, my name is Jeff A.
#>I'll have to remember malloc and free and pointer arithmetic and curly braces.
Cependant, je ne suis pas convaincu que cette solution ne contient pas toutes les mises en garde, je n'ai pas pensé (désolé pour le triple négation, en particulier parce que réaffectation des la magique __class__
ne fonctionne tout simplement pas. Même si cela fonctionne, je ne peux pas aider le sentiment qu'il y devrait être plus pythonic moyen de le faire.
Est-il?
Edit: Merci à tous pour vos réponses. Voici ce que je reçois d'eux:
Bien que l'idée de reclassing une instance par l'attribution d'
__class__
n'est pas largement utilisé de l'idiome, la plupart des réponses (4 sur 6 au moment de la rédaction) considèrent que c'est une approche valable. Un anwswer (par ojrac) dit qu'il est "assez bizarre au premier coup d'œil," avec qui je suis d'accord (c'était la raison de poser la question). Une seule réponse (par Jason Baker; avec deux commentaires positifs & voix) activement m'a découragé de le faire, mais en se basant sur l'exemple de cas d'utilisation plutôt que sur la technique en général.Aucune réponse, positive ou non, trouve un réel problème technique de cette méthode. Une petite exception est jls qui mentionne à l'attention de la vieille-classes de style, ce qui est probablement vrai, et C extensions. Je suppose que le nouveau-style-classe-aware C les extensions doivent être aussi très bien avec cette méthode comme Python lui-même (à condition que le dernier est vrai), même si vous êtes en désaccord, garder les réponses à venir.
Quant à la question de savoir comment pythonic c'est, il y a eu quelques réponses positives, mais pas de réelle motivation. En regardant le Zen (import this
- )), je suppose que la règle la plus importante dans ce cas est "Explicite est mieux qu'implicites." Je ne suis pas sûr, cependant, si cette règle, qui parle pour ou contre reclassing de cette façon.
À l'aide de
{has,get,set}attr
semble plus explicite, comme nous sommes explicitement les modifications de l'objet au lieu d'utiliser la magie.À l'aide de
__class__ = newclass
semble plus explicite parce que nous avons explicitement de dire "C'est maintenant un objet de la classe 'newclass' attendre à un comportement différent" en silence au lieu de changer les attributs, mais en laissant aux utilisateurs de l'objet à croire qu'ils font affaire avec un objet régulier de la vieille classe.
En résumé: à Partir d'un point de vue technique, la méthode semble ok; le pythonicity question reste sans réponse avec une préférence pour le "oui".
J'ai accepté Martin Geisler réponse, parce que l'Mercurial plugin est un exemple assez fort (et aussi parce qu'elle répondait à une question que je n'avais même pas demandé à moi-même encore). Cependant, s'il y a des arguments sur la pythonicity question, je voudrais encore entendre. Merci à tous jusqu'à présent.
P. S. Le réel de cas d'utilisation est une INTERFACE de contrôle des données de l'objet qui a besoin de croître fonctionnalité supplémentaire au moment de l'exécution. Cependant, la question est destinée à être très général.