Jeudi 7 Juillet 2005
[Python] - Décorateurs
Mon apprentissage du python avance grandement et je crois qu'aujourd'hui j'ai enfin compris un point très important de la programmation orientée objet. Je vais donc essayer de vous en faire part.
Présentation
Un des buts de la POO est de faire du code réutilisable. Bref en pratique il convient de faire des classes globales qui ne prennent en compte que le cas général. Puis l'on crée des classes qui héritent de notre classe globale tout en ajoutant des méthodes ou des attributs.
Par exemple, vous avez la classe voiture, qui contient un moteur, des roues et une certaine quantité d'essence. Vous avez aussi la classe 4x4 qui est une voiture à laquelle ont ajoute les quatre roues motrices ou la classe luxe qui propose un lecteur DVD dans les appuis-têtes.
Jusque là tout était rose dans le magnifique monde de la POO. Cependant que ce passe-t-il quand votre 4x4 est un 4x4 de luxe ? Vous aller créer une nouvelle classe qui dérive des trois première ? C'est encore faisable. Mais imaginons soudain que vous vous fassiez voler le lecteur DVD. Votre 4x4 n'est plus de Luxe, il faut donc pouvoir remédier à cela.
C'est là que les décorateurs interviennent. Il s'agit en fait de redéfinir une fonction au sein même d'une classe après avoir passée celle-ci dans une fonction de décoration. L'exemple suivant vous en dira plus :
class Voiture:
def qui_suis_je(self):
print "---"
print "Je suis une voiture"
def x4x(fonction):
def nouvelle_fonction():
fonction()
print "Je suis un 4x4"
return nouvelle_fonction
def luxe(fonction):
def nouvelle_fonction():
fonction()
print "Je suis de luxe"
return nouvelle_fonction
car = Voiture()
car.quisuisje()
# >>> Je suis une voiture
car.quisuisje = x4x(car.quisuisje)
car.quisuisje()
# >>> Je suis une voiture
# >>> Je suis un 4x4
car.quisuisje = luxe(car.quisuisje)
car.quisuisje()
# >>> Je suis une voiture
# >>> Je suis un 4x4
# >>> Je suis de luxe
del car.quisuisje
car.quisuisje()
# >>> Je suis une voiture
# Ajout du décorateur 4x4.
# Le lecteur DVD à été volé,
# ce n'est pas une raison pour
# perdre les roues motrices
car.qui_suis_je = x4x(car.qui_suis_je)
car.qui_suis_je()
# >>> Je suis une voiture
# >>> Je suis un 4x4
Bref, je pense que vous avez compris le concept. Malheureusement il n'y a aucune technique toute faite pour enlever simplement un décorateur. Il faut donc tous les enlever via la méthode del et puis rajouter ceux que l'on veut après. Ce n'est pas très pratique et devient vite lourd dans le code.
Solution
Mais heureusement il y a toujours des solutions et donc le but va être de créer des fonctions pour automatiser tout cela. Un charmant monsieur de #python@irc.freenode.net m'a proposé le script qui suit et j'avoue qu'il était bien meilleur que le miens, donc je le garde.
class Decorable:
def init(self, func):
self.decorators = []
self.original = func
self.decorated = func
def call(self, *args, **kw):
self.decorated(*args, **kw)
def decorate(self, decorator):
self.decorated = decorator(self.decorated)
self.decorators.append(decorator)
def undecorate(self, decorator=None):
if decorator:
self.decorators.remove(decorator)
else:
self.decorators = []
self.decorated = reduce(lambda decorated, decorator:
decorator(decorated), self.decorators, self.original)
Reprenons notre classe de tout à l'heure et voyons comment procéder.
car = Voiture() car.qui_suis_je = Decorable(car.qui_suis_je) car.qui_suis_je.decorate(x4x) car.qui_suis_je.decorate(luxe) car.qui_suis_je() # >>> Je suis une voiture # >>> Je suis un 4x4 # >>> Je suis de luxe car.quisuisje.undecorate(luxe) car.quisuisje() # >>> Je suis une voiture # >>> Je suis un 4x4
C'est mieux non ?
Bref, cette façon de faire ouvre des perspectives plutôt intéressantes car elle permet de vraiment faire du code totalement modulable.
Commentaires
Vendredi 08 Juillet 2005 11:27:55 - Titus Lien
Très intéressant, par contre les sorties consoles seraient un plus non négligeable pour bien comprendre le mécanisme :)
Merci et bonne continuation !
Vendredi 08 Juillet 2005 12:43:21 - Guillaume
En effet, je modifie :)
Réagissez
En fait non ! Trop de smap