Saltar al contenido principal

Flujo de ejecución

Para asegurarnos de que una función está definida antes de usarla por primera vez, es necesario conocer el orden en que las sentencias son ejecutadas, lo que llamamos flujo de ejecución.

La ejecución siempre comienza en la primera instrucción del programa. Las sentencias se ejecutan una por una, de arriba hacia abajo.

Las definiciones de funciones no alteran el flujo de ejecución del programa, pero recuerda que las sentencias dentro de una función no se ejecutan hasta que se llama a esa función.

Una llamada a una función es como un desvío en el flujo de ejecución. En lugar de pasar a la siguiente sentencia, el flujo salta al cuerpo de la función, ejecuta todas las sentencias que hay allí, y después vuelve al punto donde lo dejó.

Todo esto parece bastante sencillo, hasta que se recuerda que una función puede llamar a otra. Cuando se está en mitad de una función, el programa puede tener que ejecutar sentencias de otra función.

Python es capaz de llevar el seguimiento de dónde se encuentra en cada momento, de modo que cada vez que completa una ejecución de una función, el programa vuelve al punto donde dejó la función que llamó a esa utilizando la pila de funciones.

Pila de funciones

La pila de funciones (también llamada call stack en inglés) es una estructura de datos que gestiona la ejecución de funciones en un programa. Cada vez que una función es llamada, se añade un nuevo elemento (marco de la función o stack frame) a la pila. Cuando la función finaliza, ese elemento se elimina de la pila, y el control vuelve a la función que llamó a la actual.

Su funcionamiento es el siguiente:

  1. Llamada a una función: Cuando una función es llamada, su información (como los argumentos de la función y el punto de retorno en el código) se añade a la cima de la pila.
  2. Ejecución de la función: La función se ejecuta. Si dentro de esta función se llama a otra función, esta nueva llamada también se añade a la cima de la pila.
  3. Finalización de una función: Cuando una función finaliza, la información de esa llamada se elimina de la cima de la pila, y el programa continúa la ejecución desde el punto donde la función anterior dejó de ejecutar.

El marco de pila contiene:

  • Punto de retorno: Es la posición en el código a la que debe volver el programa una vez que la función finaliza. Básicamente, indica dónde debe continuar el flujo de ejecución después de la llamada a la función. Si no se guardase el punto de retorno, el programa no sabría dónde continuar cuando la función termine.
  • Argumentos de la función: Son los valores que se pasan a la función cuando es llamada. Estos valores son esenciales para que la función pueda trabajar con los datos correctos.
  • Variables locales: Son las variables que se definen dentro de la función, que solo existen mientras la función se está ejecutando. Las variables locales se guardan en la pila para asegurar que cada llamada a la función tenga su propio conjunto de datos, aislado de las otras llamadas.

Veamos cómo sería una pila de funciones cuando tenemos varias llamadas de funciones encadenadas.

def suma(a, b):
resultado = a + b # Variable local 'resultado'
print(f"Suma de {a} + {b} = {resultado}")
return resultado

def multiplicar(c, d):
resultado = c * d # Variable local 'resultado'
print(f"Multiplicación de {c} * {d} = {resultado}")
return resultado

def calcular(a, b):
suma_resultado = suma(a, b) # Llama a 'suma'
multiplicar_resultado = multiplicar(a, b) # Llama a 'multiplicar'
total = suma_resultado + multiplicar_resultado # Variable local 'total'
print(f"Total de suma + multiplicación = {total}")
return total

x = 5 # Variable global x
y = 3 # Variable global y
resultado_final = calcular(x, y) # Llama a 'calcular' y asigna el valor de retorno a la variable global resultado_final
print(f"Resultado final: {resultado_final}")