C'est un début :
class MagicWrapper(object):
def __init__(self, wrapped):
self._wrapped = wrapped
def __getattr__(self, attr):
return getattr(self._wrapped, attr)
def __setattr__(self, attr, val):
if attr == '_wrapped':
super(MagicWrapper, self).__setattr__('_wrapped', val)
else:
setattr(self._wrapped, 'old_' + attr, getattr(self._wrapped, attr))
setattr(self._wrapped, attr, val)
class MyObject(object):
def __init__(self):
self.attr_one = None
self.attr_two = 1
obj = MyObject()
obj = MagicWrapper(obj)
obj.attr_one = 'new value'
obj.attr_two = 2
print obj.old_attr_one
print obj.attr_one
print obj.old_attr_two
print obj.attr_two
Ce n'est pas infaillible lorsque vous essayez d'envelopper des objets bizarres (très peu de choses le sont en Python), mais cela devrait fonctionner pour les classes "normales". Vous pourriez écrire beaucoup plus de code pour vous approcher un peu plus du comportement de l'objet enveloppé, mais il est probablement impossible de le faire parfaitement. La principale chose dont il faut être conscient ici est que de nombreuses méthodes spéciales ne seront pas redirigées vers l'objet enveloppé.
Si vous voulez faire cela sans envelopper obj
d'une manière ou d'une autre, ça va devenir désordonné. Voici une option :
def add_old_setattr_to_class(cls):
def __setattr__(self, attr, val):
super_setattr = super(self.__class__, self).__setattr__
if attr.startswith('old_'):
super_setattr(attr, val)
else:
super_setattr('old_' + attr, getattr(self, attr))
super_setattr(attr, val)
cls.__setattr__ = __setattr__
class MyObject(object):
def __init__(self):
self.attr_one = None
self.attr_two = 1
obj = MyObject()
add_old_setattr_to_class(obj.__class__)
obj.attr_one = 'new value'
obj.attr_two = 2
print obj.old_attr_one
print obj.attr_one
print obj.old_attr_two
print obj.attr_two
Notez que cette méthode est extrêmement envahissante si vous l'utilisez sur des objets fournis en externe. Elle modifie globalement le classe de l'objet auquel vous appliquez la magie, et pas seulement de cette seule instance. C'est parce que comme plusieurs autres méthodes spéciales, __setattr__
n'est pas recherchée dans le dictionnaire d'attributs de l'instance ; la recherche passe directement à la classe, il n'y a donc aucun moyen de remplacer simplement __setattr__
sur l'instance. Je qualifierais ce genre de code de hack bizarre si je le rencontrais dans la nature (c'est une "astuce" si je l'écris moi-même, bien sûr ;) ).
Cette version peut ou non jouer avec les objets qui jouent déjà des tours à l'aide de la technologie de l'information. __setattr__
y __getattr__
/ __getattribute__
. Si vous finissez par modifier la même classe plusieurs fois, je pense que cela fonctionne toujours, mais vous vous retrouvez avec un nombre toujours plus grand de wrapped __setattr__
définitions. Vous devriez probablement essayer d'éviter cela, peut-être en mettant un "drapeau secret" sur la classe et en le vérifiant dans le fichier add_old_setattr_to_class
avant de modifier cls
. Vous devriez probablement aussi utiliser un préfixe plus improbable que juste old_
puisque vous essayez essentiellement de créer un espace de noms entièrement distinct.