Pendant le développement, il est souvent utile de savoir combien de fois une fonction ou une méthode est appelée, ainsi que de connaitre les durées des traitements.
Certains outils de développement peuvent donner des infos comme ça, mais il est facile de fabriquer son propre outil en Python: c'est l'objet de cette page.
Pour cela, on utilisera les décorateurs, qui “enveloppent” les fonctions et méthodes décorées en permettant d'intervenir avant et après les appels.
Comme il faudra bien conserver les informations entre 2 appels, on va fabriquer un décorateur sous forme de classe, mais sans passage d'arguments.
Voilà le code proposé qu'on va placer dans un fichier appelé, par exemple, libdeco.py, et qu'on utilisera comme un module:
#!/usr/bin/python # -*- coding: utf-8 -*- # Python 2.7 et 3.2 import functools, types import time ############################################################################# class compteur(object): """ decorateur pour compter le nombre d'appels et le temps d'exécution. => valable pour décorer des fonctions ou des méthodes de classe """ def __init__(self, fonc): """initialisation du décorateur""" self.fonc = fonc self.comptappel = 0 # compteur d'appels self.tempsexec = 0 # pour stocker les temps d'exécution # pour que les fonctions décorées gardent nom et docstring: functools.wraps(fonc)(self) def __get__(self, inst, owner=None): """méthode nécessaire pour décorer les méthodes: avoir le bon 'self' """ return types.MethodType(self, inst) def __call__(self, *args, **kwargs): """ méthode appelée à chaque appel de la fonction décorée """ # instructions avant l'appel de la fonction décorée self.comptappel += 1 # incrémentation du compteur d'appels temps = time.clock() # initialisation du compteur de temps # appel de la fonction décorée result = self.fonc(*args, **kwargs) # instructions après le retour d'appel de la fonction décorée self.tempsexec += time.clock()-temps # ajout du temps d'exécution # fin d'appel return result def resultat(self): """retourne le résultat: compteur d'appel et temps moyen d'exécution""" c = self.comptappel t = self.tempsexec if c==0: tm = 0 else: tm = t/c return c, tm
Ce code fonctionne sans modification sous Python 2.7 et 3.2.
Comme il s'agit d'un outil de développement, il peut être pratique de l'intégrer directement dans le système Python lui-même avec distutils, afin qu'il soit rendu facilement disponible dans tous les projets de développement.
#!/usr/bin/python # -*- coding: utf-8 -*- # Python 2.7 et 3.2 from libdeco import compteur @compteur def fonction1(): for i in range(0,1000000): x = sin(4.65789321456) @compteur def fonction2(): for i in range(0,1000000): x = cos(4.65789321456) y = fonction1() # 3 exécutions de fonction1 et 2 de fonction2 (qui appelle fonction1) fonction1() fonction1() fonction2() fonction1() fonction2() # affichage des résultats print(fonction1.resultat()) # affiche: (5, 0.19338031069732708) print(fonction2.resultat()) # affiche: (2, 0.38713576021911655)
#!/usr/bin/python # -*- coding: utf-8 -*- # Python 2.7 et 3.2 from libdeco import compteur from math import * class maclasse(object): @compteur def methode(self): for i in range(0,1000000): x = cos(4.65789321456) # appels v = maclasse() # 4 exécutions de la méthode 'methode' v.methode() v.methode() v.methode() v.methode() # affichage des résultats print(v.methode.resultat()) # affiche: (4, 0.19418323096733464)
Amusez-vous bien!