Ver índice de contenidos del libro

Capítulo 18. Modelo de ejecución de funciones y recursividad

18.1. La pila de ejecución de las funciones

Si miramos el siguiente segmento de código y su ejecución podemos comprobar que, pese a tener el mismo nombre, la variable de x de la función f y la variable de x de la función g no tienen nada que ver: una y otra se refieren a valores distintos, y modificar una no modifica a la otra.

def f():
  x = 50
  a = 20
  print "En f, x vale", x
 
def g():
  x = 10
  b = 45
  print "En g, antes de llamar a f, x vale", x
  f()
  print "En g, después de llamar a f, x vale", x

Esta es la ejecución de g():

>>> g()
En g, antes de llamar a f, x vale 10
En f, x vale 50
En g, después de llamar a f, x vale 10

Este comportamiento lo hemos ido viendo desde el principio, sin embargo, nunca se explicó porqué sucede. Vamos a ver en esta sección cómo se ejecutan las llamadas a funciones, para comprender cuál es la razón de este comportamiento.

Cada función tiene asociado por un lado un código (el texto del programa) que se ejecutará, y por el otro un conjunto de variables que le son propias (en este caso x y a se asocian con f y x y b se asocian con g) y que no se confunden entre sí pese a tener el mismo nombre (no debería llamarnos la atención ya que después de todo conocemos a muchas personas que tienen el mismo nombre, en este caso la función a la que pertenecen funciona como una especie de "apellido").

Estos nombres asociados a una función los va descubriendo el intérprete de Python a medida que va ejecutando el programa (hay otros lenguajes en los que los nombres se descubren todos juntos antes de iniciar la ejecución).

La ejecución del programa se puede modelar por la siguiente tabla:

Instrucción Contexto de g Contexto de f Resultado impreso
g() Creado pero vacío No existe -
x = 10 x → 10 No existe -
b = 45 x → 10 b → 45 No existe -
print x → 10 b → 45 No existe En g, antes de llamar a f, x vale 10
f() x → 10 b → 45 Creado pero vacío -
x = 50 x → 10 b → 45 x → 50 -
a = 20 x → 10 b → 45 x → 50 a → 20 -
print x → 10 b → 45 x → 50 a → 20 En f, x vale 50
print x → 10 b → 45 No existe En g, despues de llamar a f, x vale 10
- No existe No existe -

Se puede observar que:

Cuando se invoca a g, se arma un contexto vacío para contener las referencias a las variables asociadas con g. Ese contexto se apila sobre una pila vacía.

Cuando se ejecuta dentro de g la invocación f() se apila un contexto vacío que va a alojar las variables asociadas con f (y se transfiere el control del programa a la primera instrucción de f). El contexto de g queda debajo del tope de la pila, y por lo tanto el intérprete no lo ve.

Mientras se ejecuta f, todo el tiempo el intérprete busca los valores que necesita usando el contexto que está en el tope de la pila.

Después de ejecutar el final de la ejecución de f, se desapila el contexto de f y reaparece el contexto de g en el tope de la pila. Sigue ejecutando g a partir de donde se suspendió por la invocación a f. g sólo ve su contexto en el tope de la pila.

Después de ejecutar todas las instrucciones, se encuentra el final de la ejecución de g. Se desapila el contexto de g y queda la pila vacía.

El ámbito de definición de una variable está constituido por todas las partes del programa desde donde esa variable se ve.

Copyright (c) 2011-2014 Rosita Wachenchauzer, Margarita Manterola, Maximiliano Curia, Marcos Medrano, Nicolás Paez. La copia y redistribución de esta página se permite bajo los términos de la licencia Creative Commons Atribución - Compartir Obras Derivadas Igual 3.0 siempre que se conserve esta nota de copyright.