← Retourner à la liste des articles
Image blog
Auteur

Par Maxime Jumelle

CTO & Co-Founder

Publié le 29 oct. 2020

Catégorie Machine Learning

Les décorateurs de fonctions sous Python

Tu as peut-être déjà remarqué le symbole @ à proximité des fonctions avec Python. Il s'agit de décorateurs de fonctions.

Mais à quoi peuvent-ils servir ? En quelques lignes de code, nous allons voir comment créer des décorateurs de fonctions Python qui peuvent nous faciliter la vie.

C'est quoi un décorateur de fonction ?

Prenons un cas concret : on souhaite mesurer la durée d'exécution d'une fonction. Pour cela, nous importons la librairie time et avec le code suivant, nous allons mesurer le temps d'exécution de la fonction pause.

import time

def pause():
    print("Début ...")
    time.sleep(2)  # Pause de 2 secondes
    print("Fin !")

start_time = time.time()  # Temps avant exécution
pause()
end_time = time.time()  # Temps après exécution
print("Durée d'exécution : {:1.3}s".format(end_time - start_time))

Le problème, c'est qu'à chaque fois que l'on souhaitera mesurer la durée d'exécution d'une fonction, il faudra copier-coller l'affectation aux variables start_time et end_time, puis afficher la durée avec un print.


À lire aussi : découvrez notre formation MLOps


C'est ici que les décorateurs de fonctions vont nous être utile. Elles viennent englober une fonction et vont permettrent, par exemple, d'exécuter des instructions avant ou après l'exécution de la fonction.

def timing(func):
    """
    Mesure le temps d'exécution d'une fonction.
    """
    def wrapper():
        start_time = time.time()
        func()
        end_time = time.time()
        print("Durée d'exécution : {:1.3}s".format(end_time - start_time))

    return wrapper

Analysons ce code.

  • La fonction timing attends un paramètre, la fonction dont nous allons calculer la durée d'exécution.
  • À l'intérieur de timing, nous avons crée une fonction wrapper qui sera renvoyée par la fonction timing.
  • Cette fonction wrapper va calculer le temps avant puis après l'exécution de la fonction et afficher la durée avec un print.

Maintenant, pour mesurer la durée d'exécution de la fonction pause, il suffit d'appeler la fonction timing avec, comme paramètre, la fonction pause.

timing(pause)()

En écrivant timing(pause), j'obtiens la fonction wrapper qui va exécuter à l'intérieur la fonction pause.

⚠️

timing(pause)
est une fonction Python, d'où la présence des parenthèses pour l'exécuter.

Mais plutôt que d'utiliser cette écriture à chaque exécution de la fonction pause, un décorateur de fonction va effectuer la même opération.

@timing  # La fonction timing(pause) sera exécuté à chaque appel de la fonction pause
def pause():
    print("Début ...")
    time.sleep(2)  # Pause de 2 secondes
    print("Fin !")

Dorénavant, dès que l'on appelera la fonction pause, la durée d'exécution sera automatiquement calculée et affichée.

pause()

Un décorateur avec des arguments dans la fonction

Une situation qui arrive souvent, c'est lorsque la fonction décorée, en l'occurrence ici la fonction pause, s'attend à avoir des paramètres.

@timing
def pause(t):
    print("Début ...")
    time.sleep(t)  # Pause de t secondes
    print("Fin !")

Le paramètre t indique ici la durée de la pause. Si maintenant, on appelle la fonction pause avec comme argument t=2.

pause(t=2)

Une erreur apparaît ! 😨

Pourquoi y a-t-il une erreur ? Pour rappel, appeler la fonction pause revient à appeler timing(pause). Or, le paramètre spécifié ici n'est pas envoyé à pause mais à timing(pause) (c'est-à-dire au wrapper).

❓ Comment faire pour que les paramètres ne soient pas envoyés au wrapper mais à la fonction pause ?

Il suffit de rajouter des arguments dans la fonction wrapper :

def timing(func):
    """
    Mesure le temps d'exécution d'une fonction.
    """
    def wrapper(*args, **kwargs):
        start_time = time.time()
        func(*args, **kwargs)
        end_time = time.time()
        print("Durée d'exécution : {:1.3}s".format(end_time - start_time))

    return wrapper

Nous avons défini deux arguments dans le wrapper.


À lire aussi : découvrez notre formation MLOps


  • L'argument *args permet de spécifier des paramètres positionnels (selon leur position lors de l'appel de la fonction).
  • L'argument **kwargs permet de spécifier des paramètres nommés (de la forme param=valeur).

Ces arguments sont ré-utilisés lors de l'appel de la fonction func.

Ainsi, en exécutant pause(2), le paramètre 2 sera envoyé directement à la pause pause par l'intermédiaire du wrapper.

@timing
def pause(t):
    print("Début ...")
    time.sleep(t)  # Pause de t secondes
    print("Fin !")

pause(t=2)

Plus aucune difficulté maintenant pour passer des arguments en paramètres avec les décorateurs de fonctions. 🙂

Vous souhaitez vous former au MLOps ?

Articles similaires

Blog

20 sept. 2022

Machine Learning

Hugging Face est une startup française qui s'est fait connaître grâce à l'infrastructure NLP qu'ils ont développée. Aujourd'hui, elle est sur le point de révolutionner le domaine du Machine Learning et traitement automatique du langage naturel. Dans cet article, nous allons présenter Hugging Face et détailler les taches de base que cette librairie permet de réaliser. Nous allons également énumérer ses avantages et ses alternatifs.

Nada Belaidi

Équipe Blent

Data Scientist

Lire l'article

Blog

12 juil. 2022

Machine Learning

spaCy est une bibliothèque open-source pour le traitement avancé du langage naturel. Elle est conçue spécifiquement pour une utilisation en production et permet de construire des applications qui traitent et comprennent de grands volumes de texte.

Nada Belaidi

Équipe Blent

Data Scientist

Lire l'article

Blog

4 juil. 2022

Machine Learning

Un auto-encodeur est une structure de réseaux neuronaux profonds qui s'entraîne pour réduire la quantité de données nécessaires pour représenter une donnée d'entrée. Ils sont couramment utilisés en apprentissage automatique pour effectuer des tâches de compression de données, d'apprentissage de représentations et de détection de motifs.

Nada Belaidi

Équipe Blent

Data Scientist

Lire l'article