Comment vérifier si une fonction a été appelée en Python

PythonPythonBeginner
Pratiquer maintenant

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans ce laboratoire (lab), vous apprendrez à vérifier si une fonction a été appelée en Python, une compétence essentielle pour le débogage, l'analyse des performances et les tests. Le laboratoire explore des techniques pour suivre les appels de fonction, y compris le nombre de fois qu'une fonction est appelée et les arguments utilisés.

Le laboratoire commence par un exemple simple de fonction Python et présente une technique de base pour suivre manuellement les appels de fonction en utilisant une variable globale pour compter les invocations. Il vous guide ensuite dans la modification du code pour incrémenter le compteur à chaque appel de la fonction et afficher le nombre total d'invocations. Le laboratoire continuera à explorer des méthodes plus avancées pour surveiller les appels de fonction, telles que la création de fonctions enveloppantes (wrapper functions) et l'utilisation du module unittest.mock.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/BasicConceptsGroup -.-> python/variables_data_types("Variables and Data Types") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/AdvancedTopicsGroup -.-> python/decorators("Decorators") subgraph Lab Skills python/variables_data_types -.-> lab-559518{{"Comment vérifier si une fonction a été appelée en Python"}} python/function_definition -.-> lab-559518{{"Comment vérifier si une fonction a été appelée en Python"}} python/arguments_return -.-> lab-559518{{"Comment vérifier si une fonction a été appelée en Python"}} python/decorators -.-> lab-559518{{"Comment vérifier si une fonction a été appelée en Python"}} end

Comprendre le suivi des appels de fonction

Dans cette étape, nous allons explorer le concept fondamental du suivi des appels de fonction en Python. Comprendre combien de fois une fonction est appelée et avec quels arguments est essentiel pour le débogage, l'analyse des performances et les tests. Nous commencerons par un exemple simple, puis nous présenterons une technique de base pour suivre manuellement les appels de fonction.

Commençons par créer une simple fonction Python dans le répertoire ~/project en utilisant l'éditeur VS Code. Créez un fichier nommé my_function.py et ajoutez le code suivant :

## ~/project/my_function.py
def greet(name):
  """This function greets the person passed in as a parameter."""
  print(f"Hello, {name}!")

## Example usage
greet("Alice")
greet("Bob")

Ce code définit une fonction greet qui prend un nom en entrée et affiche un message de salutation. Il appelle ensuite la fonction deux fois avec des noms différents.

Pour exécuter ce script, ouvrez votre terminal dans VS Code (vous pouvez généralement le faire en appuyant sur Ctrl + `` ou Cmd + ``) et exécutez la commande suivante :

python ~/project/my_function.py

Vous devriez voir la sortie suivante :

Hello, Alice!
Hello, Bob!

Maintenant, modifions le fichier my_function.py pour suivre combien de fois la fonction greet est appelée. Nous allons introduire une variable globale pour suivre le nombre d'invocations.

Modifiez le fichier my_function.py pour inclure le code suivant :

## ~/project/my_function.py
invocation_count = 0

def greet(name):
  """This function greets the person passed in as a parameter."""
  global invocation_count
  invocation_count += 1
  print(f"Hello, {name}!")

## Example usage
greet("Alice")
greet("Bob")

print(f"The greet function was called {invocation_count} times.")

Dans cette version modifiée, nous avons ajouté une variable globale invocation_count et l'avons incrémentée chaque fois que la fonction greet est appelée. Nous avons également ajouté une instruction d'impression à la fin pour afficher le nombre total d'invocations.

Exécutez le script à nouveau en utilisant la même commande :

python ~/project/my_function.py

Vous devriez maintenant voir la sortie suivante :

Hello, Alice!
Hello, Bob!
The greet function was called 2 times.

Cet exemple simple montre une manière de base de suivre les appels de fonction. Cependant, cette approche peut devenir fastidieuse pour des applications plus complexes avec de nombreuses fonctions. Dans les étapes suivantes, nous explorerons des techniques plus sophistiquées utilisant des fonctions enveloppantes (wrapper functions) et le module unittest.mock.

Créer une fonction enveloppante (wrapper function)

Dans cette étape, nous apprendrons à utiliser des fonctions enveloppantes pour suivre les appels de fonction. Une fonction enveloppante est une fonction qui entoure une autre fonction, vous permettant d'ajouter des fonctionnalités avant ou après l'exécution de la fonction originale. C'est une technique puissante pour surveiller les appels de fonction sans modifier le code de la fonction originale.

Continuons avec la fonction greet de l'étape précédente. Nous allons créer une fonction enveloppante qui suivra le nombre de fois où greet est appelée.

Modifiez le fichier my_function.py dans le répertoire ~/project pour inclure le code suivant :

## ~/project/my_function.py
invocation_count = 0

def greet(name):
  """This function greets the person passed in as a parameter."""
  print(f"Hello, {name}!")

def greet_wrapper(func):
  """This is a wrapper function that tracks the number of times the wrapped function is called."""
  def wrapper(*args, **kwargs):
    global invocation_count
    invocation_count += 1
    result = func(*args, **kwargs)
    return result
  return wrapper

## Apply the wrapper to the greet function
greet = greet_wrapper(greet)

## Example usage
greet("Alice")
greet("Bob")

print(f"The greet function was called {invocation_count} times.")

Dans ce code :

  • Nous définissons une fonction greet_wrapper qui prend une fonction (func) en entrée.
  • À l'intérieur de greet_wrapper, nous définissons une autre fonction appelée wrapper. Cette fonction wrapper est l'enveloppe réelle qui sera exécutée lorsque nous appellerons la fonction enveloppée.
  • La fonction wrapper incrémente le invocation_count, appelle la fonction originale (func) avec tous les arguments qui lui sont passés et renvoie le résultat.
  • Nous appliquons ensuite l'enveloppe à la fonction greet en assignant greet = greet_wrapper(greet). Cela remplace la fonction greet originale par la version enveloppée.

Exécutez le script à nouveau en utilisant la même commande :

python ~/project/my_function.py

Vous devriez voir la sortie suivante :

Hello, Alice!
Hello, Bob!
The greet function was called 2 times.

Cette sortie est la même que dans l'étape précédente, mais maintenant nous utilisons une fonction enveloppante pour suivre les invocations. Cette approche est plus flexible car vous pouvez facilement appliquer la même enveloppe à plusieurs fonctions.

Maintenant, ajoutons une autre fonction et appliquons la même enveloppe à celle-ci. Ajoutez la fonction suivante au fichier my_function.py :

## ~/project/my_function.py
invocation_count = 0

def greet(name):
  """This function greets the person passed in as a parameter."""
  print(f"Hello, {name}!")

def add(x, y):
  """This function adds two numbers and returns the result."""
  print(f"Adding {x} and {y}")
  return x + y

def greet_wrapper(func):
  """This is a wrapper function that tracks the number of times the wrapped function is called."""
  def wrapper(*args, **kwargs):
    global invocation_count
    invocation_count += 1
    result = func(*args, **kwargs)
    return result
  return wrapper

## Apply the wrapper to the greet function
greet = greet_wrapper(greet)
add = greet_wrapper(add)

## Example usage
greet("Alice")
greet("Bob")
add(5, 3)

print(f"The greet function was called {invocation_count} times.")

Nous avons ajouté une fonction add et appliqué la greet_wrapper à celle-ci également. Maintenant, le invocation_count suivra le nombre total d'appels à la fois à greet et à add.

Exécutez le script à nouveau :

python ~/project/my_function.py

Vous devriez voir une sortie similaire à celle-ci :

Hello, Alice!
Hello, Bob!
Adding 5 and 3
The greet function was called 3 times.

Comme vous pouvez le voir, le invocation_count reflète maintenant le nombre total d'appels aux fonctions greet et add. Les fonctions enveloppantes offrent un moyen propre et réutilisable de surveiller les appels de fonction.

Utiliser unittest.mock pour surveiller les appels

Dans cette étape, nous allons explorer comment utiliser le module unittest.mock pour surveiller les appels de fonction. Le module unittest.mock est un outil puissant pour les tests et le débogage, et il offre un moyen pratique de suivre les appels de fonction, les arguments et les valeurs de retour.

Continuons avec la fonction greet des étapes précédentes. Nous allons utiliser unittest.mock pour surveiller les appels à greet.

Modifiez le fichier my_function.py dans le répertoire ~/project pour inclure le code suivant :

## ~/project/my_function.py
from unittest import mock

def greet(name):
  """This function greets the person passed in as a parameter."""
  print(f"Hello, {name}!")

## Create a mock object for the greet function
mock_greet = mock.Mock(wraps=greet)

## Example usage
mock_greet("Alice")
mock_greet("Bob")

## Print the number of times the function was called
print(f"The greet function was called {mock_greet.call_count} times.")

## Print the arguments the function was called with
print(f"The calls were: {mock_greet.call_args_list}")

Dans ce code :

  • Nous importons le module mock de unittest.
  • Nous créons un objet mock.Mock qui enveloppe la fonction greet. L'argument wraps indique à l'objet mock d'appeler la fonction greet originale lorsqu'il est appelé.
  • Nous appelons ensuite l'objet mock_greet au lieu de la fonction greet originale.
  • Enfin, nous utilisons les attributs call_count et call_args_list de l'objet mock pour obtenir des informations sur les appels de fonction.

Exécutez le script à nouveau en utilisant la même commande :

python ~/project/my_function.py

Vous devriez voir une sortie similaire à celle-ci :

Hello, Alice!
Hello, Bob!
The greet function was called 2 times.
The calls were: [call('Alice'), call('Bob')]

Cette sortie montre que la fonction greet a été appelée deux fois, et elle montre également les arguments qui ont été passés à la fonction à chaque appel.

Maintenant, examinons plus attentivement la call_args_list. C'est une liste d'objets call, chacun représentant un appel unique à la fonction mockée. Vous pouvez accéder aux arguments de chaque appel en utilisant les attributs args et kwargs de l'objet call.

Par exemple, modifions le code pour afficher les arguments du premier appel :

## ~/project/my_function.py
from unittest import mock

def greet(name):
  """This function greets the person passed in as a parameter."""
  print(f"Hello, {name}!")

## Create a mock object for the greet function
mock_greet = mock.Mock(wraps=greet)

## Example usage
mock_greet("Alice")
mock_greet("Bob")

## Print the number of times the function was called
print(f"The greet function was called {mock_greet.call_count} times.")

## Print the arguments the function was called with
print(f"The calls were: {mock_greet.call_args_list}")

## Print the arguments of the first call
if mock_greet.call_args_list:
  print(f"The arguments of the first call were: {mock_greet.call_args_list[0].args}")

Exécutez le script à nouveau :

python ~/project/my_function.py

Vous devriez voir une sortie similaire à celle-ci :

Hello, Alice!
Hello, Bob!
The greet function was called 2 times.
The calls were: [call('Alice'), call('Bob')]
The arguments of the first call were: ('Alice',)

Cette sortie montre que le premier appel à greet a été effectué avec l'argument "Alice".

Le module unittest.mock offre un moyen puissant et flexible de surveiller les appels de fonction en Python. C'est un outil précieux pour les tests, le débogage et l'analyse des performances.

Résumé

Dans ce laboratoire (lab), nous avons commencé par comprendre l'importance de suivre les appels de fonction pour le débogage, l'analyse des performances et les tests en Python. Nous avons créé une simple fonction greet qui affiche un message de salutation, puis nous avons suivi manuellement le nombre de fois où elle a été appelée en introduisant une variable globale invocation_count et en l'incrémentant à l'intérieur de la fonction. Cela nous a permis de surveiller combien de fois la fonction a été exécutée.