El Contexto (Ámbito, Scope): Cada Máquina su Mesa

Fig. 9.6: Lo global se lee desde el piso; lo local vive y muere en la mesa.
Hay una regla del almacén que ningún capítulo te había contado y que explica el error más desconcertante de tus primeras semanas con funciones: cada función tiene su propio ámbito (scope), su territorio privado de variables. Aquí va la regla completa: cada función trabaja en su propia mesa, y al terminar, la mesa se recoge (Fig. 9.6).
Las variables locales nacen y mueren adentro
Cuando una función crea una variable, esa caja vive en la mesa de trabajo de la función. No en el piso del almacén. Mira lo que pasa al intentar usarla desde afuera:
def calcular_iva(precio: float) -> float:
impuesto = precio * 0.16 # 'impuesto' nace en la mesa de la función
return precio + impuesto
total = calcular_iva(100)
print(total) # 116.0: esto funciona
print(impuesto) # NameError: name 'impuesto' is not defined
impuesto existió, hizo su trabajo y desapareció cuando la función terminó. Python no está fallando: está limpiando. A esto se le llama contexto local: la variable solo existe dentro de la función que la creó. Y aplica igual a los parámetros: precio tampoco existe afuera.
Puede parecer drástico, pero es un regalo: significa que puedes usar total, resultado o i dentro de veinte funciones distintas sin que se pisen entre ellas. Cada mesa es privada.
Leer el piso del almacén se puede; modificarlo, no
La dirección contraria sí funciona: una función puede leer las variables que viven afuera, en el piso principal del programa (el contexto global):
IVA = 0.16 # vive en el piso del almacén
def calcular_total(precio: float) -> float:
return precio * (1 + IVA) # leer lo global: permitido
print(calcular_total(100)) # 116.0
Pero si intentas asignar a esa variable dentro de la función, Python no modifica la global: crea una local nueva con el mismo nombre, y la global ni se entera. Tiene sentido si sigues la imagen de la mesa: asignar es anotar en tu propia hoja de trabajo, no cruzar el piso para tachar la etiqueta que ya estaba puesta allá abajo. Ahí nacen bugs silenciosos: el código corre sin un solo error, pero la global nunca cambia.
contador = 0
def registrar() -> None:
contador = 1
registrar()
Antes de imprimir, predice: después de llamar a registrar(), ¿contador vale 0 o 1?
print(contador)
Imprime 0, no 1: la asignación dentro de la función creó una variable local nueva, la global nunca se tocó.
Python trae una palabra clave (global) para forzar la escritura, y este libro no te la va a enseñar como práctica: una función que modifica el piso del almacén a escondidas es una función en la que no se puede confiar.
La cinta oficial: parámetros y return
Entonces, ¿cómo se comunica una función con el exterior? Por la puerta de siempre, y ahora sabes por qué es la única puerta:
- Lo que la función necesita, entra como parámetro.
- Lo que la función produce, sale con
return.
def registrar(contador: int) -> int:
return contador + 1
contador = 0
contador = registrar(contador) # la cinta entrega y tú decides dónde guardar
print(contador) # 1
Todo lo que viaja por la cinta queda a la vista. Nada de cajas que aparecen o se pierden por debajo de la mesa. Cuando una función solo depende de sus parámetros y solo entrega por return, puedes leerla, probarla y reutilizarla sin mirar el resto del programa. Esa es la diferencia entre una máquina confiable y una que hay que vigilar.
Nota: las constantes globales de configuración, como la
RUTA_INVENTARIOque verás en el proyecto final, son la excepción sana: se escriben en MAYÚSCULAS, se leen desde cualquier función y nunca se reasignan. Leer configuración global está bien; mutar estado global, no.
Resumen del Capítulo
- Una función se define con
def nombre(parametro: tipo) -> tipo:y un docstring que dice qué hace; se ejecuta llamándola por nombre. - Escribe una función cuando copias el mismo código, cuando le puedes poner nombre a lo que hace, o cuando quieres probarlo por separado.
- Los parámetros son los huecos del formulario; los argumentos son los datos reales al llamar, por posición o por nombre; los valores por defecto van al final.
- Pasar una lista a una función no la copia: la función recibe otra etiqueta al mismo estante, y puede mutarlo.
print()anuncia un resultado para que lo leas tú;returnlo entrega al código que llamó, para que otro proceso lo use.- Una función puede devolver varios valores como tupla, y puede tener varios
return(salidas tempranas, las cláusulas de guarda del capítulo anterior). - El contexto local: las variables que crea una función mueren al terminar. Una función puede leer el contexto global, pero asignarle crea una variable local nueva sin tocarlo.
- La única comunicación confiable de una función con el exterior es la entrada por parámetros y la salida por
return.