Cómo comprobar si una función se ha llamado en Python

PythonPythonBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, aprenderás cómo verificar si una función ha sido llamada en Python, una habilidad crucial para la depuración, el análisis de rendimiento y las pruebas. El laboratorio explora técnicas para realizar un seguimiento de las invocaciones de funciones, incluyendo el número de veces que se llama a una función y los argumentos utilizados.

El laboratorio comienza con un ejemplo sencillo de función en Python e introduce una técnica básica para realizar un seguimiento manual de las llamadas a funciones utilizando una variable global para contar las invocaciones. Luego, te guía a través de la modificación del código para incrementar el contador cada vez que se llama a la función y mostrar el número total de invocaciones. El laboratorio continuará explorando métodos más avanzados para monitorear las llamadas a funciones, como crear funciones envolventes (wrapper functions) y utilizar el módulo 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{{"Cómo comprobar si una función se ha llamado en Python"}} python/function_definition -.-> lab-559518{{"Cómo comprobar si una función se ha llamado en Python"}} python/arguments_return -.-> lab-559518{{"Cómo comprobar si una función se ha llamado en Python"}} python/decorators -.-> lab-559518{{"Cómo comprobar si una función se ha llamado en Python"}} end

Comprender el seguimiento de invocaciones de funciones

En este paso, exploraremos el concepto fundamental de realizar un seguimiento de las invocaciones de funciones en Python. Comprender cuántas veces se llama a una función y con qué argumentos es crucial para la depuración, el análisis de rendimiento y las pruebas. Comenzaremos con un ejemplo sencillo y luego introduciremos una técnica básica para realizar un seguimiento manual de las llamadas a funciones.

Comencemos creando una función sencilla en Python en el directorio ~/project utilizando el editor VS Code. Crea un archivo llamado my_function.py y agrega el siguiente código:

## ~/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")

Este código define una función greet que toma un nombre como entrada e imprime un saludo. Luego, llama a la función dos veces con diferentes nombres.

Para ejecutar este script, abre tu terminal en VS Code (por lo general, puedes hacerlo presionando Ctrl + `` o Cmd + ``) y ejecuta el siguiente comando:

python ~/project/my_function.py

Deberías ver la siguiente salida:

Hello, Alice!
Hello, Bob!

Ahora, modifiquemos el archivo my_function.py para realizar un seguimiento de cuántas veces se llama a la función greet. Introduciremos una variable global para llevar la cuenta de las invocaciones.

Modifica el archivo my_function.py para incluir el siguiente código:

## ~/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.")

En esta versión modificada, hemos agregado una variable global invocation_count y la incrementamos cada vez que se llama a la función greet. También hemos agregado una instrucción de impresión al final para mostrar el número total de invocaciones.

Ejecuta el script nuevamente utilizando el mismo comando:

python ~/project/my_function.py

Ahora deberías ver la siguiente salida:

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

Este ejemplo sencillo demuestra una forma básica de realizar un seguimiento de las invocaciones de funciones. Sin embargo, este enfoque puede volverse engorroso para aplicaciones más complejas con muchas funciones. En los siguientes pasos, exploraremos técnicas más sofisticadas utilizando funciones envolventes (wrapper functions) y el módulo unittest.mock.

Crear una función envolvente (wrapper function)

En este paso, aprenderemos cómo utilizar funciones envolventes (wrapper functions) para realizar un seguimiento de las invocaciones de funciones. Una función envolvente es una función que envuelve a otra función, lo que te permite agregar funcionalidad antes o después de que se ejecute la función original. Esta es una técnica poderosa para monitorear las llamadas a funciones sin modificar el código de la función original.

Continuemos con la función greet del paso anterior. Crearemos una función envolvente que realice un seguimiento del número de veces que se llama a greet.

Modifica el archivo my_function.py en el directorio ~/project para incluir el siguiente código:

## ~/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.")

En este código:

  • Definimos una función greet_wrapper que toma una función (func) como entrada.
  • Dentro de greet_wrapper, definimos otra función llamada wrapper. Esta función wrapper es la envolvente real que se ejecutará cuando llamemos a la función envuelta.
  • La función wrapper incrementa el invocation_count, llama a la función original (func) con cualquier argumento que se le pase y devuelve el resultado.
  • Luego, aplicamos la envolvente a la función greet asignando greet = greet_wrapper(greet). Esto reemplaza la función greet original con la versión envuelta.

Ejecuta el script nuevamente utilizando el mismo comando:

python ~/project/my_function.py

Deberías ver la siguiente salida:

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

Esta salida es la misma que en el paso anterior, pero ahora estamos utilizando una función envolvente para realizar un seguimiento de las invocaciones. Este enfoque es más flexible porque puedes aplicar fácilmente la misma envolvente a múltiples funciones.

Ahora, agreguemos otra función y apliquemos la misma envolvente a ella. Agrega la siguiente función al archivo 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.")

Hemos agregado una función add y también le hemos aplicado la greet_wrapper. Ahora, el invocation_count realizará un seguimiento del número total de llamadas tanto a greet como a add.

Ejecuta el script nuevamente:

python ~/project/my_function.py

Deberías ver una salida similar a esta:

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

Como puedes ver, el invocation_count ahora refleja el número total de llamadas tanto a la función greet como a la función add. Las funciones envolventes proporcionan una forma limpia y reutilizable de monitorear las invocaciones de funciones.

Utilizar unittest.mock para monitorear llamadas

En este paso, exploraremos cómo utilizar el módulo unittest.mock para monitorear las llamadas a funciones. El módulo unittest.mock es una herramienta poderosa para pruebas y depuración, y proporciona una forma conveniente de realizar un seguimiento de las invocaciones de funciones, los argumentos y los valores de retorno.

Continuemos con la función greet de los pasos anteriores. Utilizaremos unittest.mock para monitorear las llamadas a greet.

Modifica el archivo my_function.py en el directorio ~/project para incluir el siguiente código:

## ~/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}")

En este código:

  • Importamos el módulo mock de unittest.
  • Creamos un objeto mock.Mock que envuelve la función greet. El argumento wraps le dice al objeto mock que llame a la función greet original cuando se le llame.
  • Luego llamamos al objeto mock_greet en lugar de la función greet original.
  • Finalmente, utilizamos los atributos call_count y call_args_list del objeto mock para obtener información sobre las llamadas a la función.

Ejecuta el script nuevamente utilizando el mismo comando:

python ~/project/my_function.py

Deberías ver una salida similar a esta:

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

Esta salida muestra que la función greet se llamó dos veces, y también muestra los argumentos que se le pasaron a la función en cada llamada.

Ahora, examinemos más de cerca la call_args_list. Es una lista de objetos call, cada uno de los cuales representa una única llamada a la función mockeada. Puedes acceder a los argumentos de cada llamada utilizando los atributos args y kwargs del objeto call.

Por ejemplo, modifiquemos el código para imprimir los argumentos de la primera llamada:

## ~/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}")

Ejecuta el script nuevamente:

python ~/project/my_function.py

Deberías ver una salida similar a esta:

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',)

Esta salida muestra que la primera llamada a greet se hizo con el argumento "Alice".

El módulo unittest.mock proporciona una forma poderosa y flexible de monitorear las llamadas a funciones en Python. Es una herramienta valiosa para pruebas, depuración y análisis de rendimiento.

Resumen

En este laboratorio (lab), comenzamos por entender la importancia de realizar un seguimiento de las invocaciones de funciones para la depuración, el análisis de rendimiento y las pruebas en Python. Creamos una simple función greet que imprime un saludo y luego realizamos un seguimiento manual del número de veces que se llamó, introduciendo una variable global invocation_count y aumentándola dentro de la función. Esto nos permitió monitorear cuántas veces se ejecutó la función.