Módulo 1 · Lección 37 de 59
El ámbito de las variables
Qué variables ve una función y cuáles no, por qué global casi nunca es la respuesta y qué pasa con listas y diccionarios.
Una función no es solo un trozo de código con nombre: es también un espacio aislado. Las variables que nacen dentro de ella no existen fuera, y eso, que al principio parece un estorbo, es justo lo que hace que las funciones sean fiables.
Si no fuera así, una variable llamada total dentro de una función pisaría la total del programa principal, y en un fichero de quinientas líneas no habría forma de saber quién ha cambiado qué. El ámbito, o scope, es la regla que decide qué nombre se ve desde dónde.
Lo que nace dentro, muere dentro#
def calcular_factura():
subtotal = 100
print(f"Dentro: {subtotal}")
calcular_factura()
print(f"Fuera: {subtotal}")Dentro: 100
Traceback (most recent call last):
File "/Users/erwin/curso-python/factura.py", line 7, in <module>
print(f"Fuera: {subtotal}")
^^^^^^^^
NameError: name 'subtotal' is not definedsubtotal es una variable local: existe desde que se crea hasta que la función termina, y entonces desaparece. Los parámetros funcionan igual, también son variables locales.
Como las locales desaparecen al terminar, cada llamada empieza de cero. Dos llamadas a la misma función no comparten nada, y por eso puedes llamarla mil veces sin que la anterior contamine a la siguiente.
Leer una variable de fuera sí se puede#
Las variables creadas en el nivel principal del fichero son globales, y desde dentro de una función se pueden leer sin hacer nada especial:
IVA = 0.21
def calcular_precio_final(precio):
return precio * (1 + IVA)
print(calcular_precio_final(100))121.0Cuando Python encuentra el nombre IVA dentro de la función, primero lo busca entre las locales; como no está, sale a buscarlo fuera, y lo encuentra.
Este es prácticamente el único uso razonable de una variable global: una constante, escrita en mayúsculas, que se define una vez al principio del fichero y que nadie modifica. Configuración, tipos de IVA, rutas, límites. Para todo lo demás, sigue leyendo.
Reasignarla es otra historia#
Intentar cambiar el valor de una global desde dentro de una función produce uno de los errores más desconcertantes de Python:
contador_de_ventas = 0
def registrar_venta():
contador_de_ventas = contador_de_ventas + 1
print(contador_de_ventas)
registrar_venta()Traceback (most recent call last):
File "/Users/erwin/curso-python/ventas.py", line 9, in <module>
registrar_venta()
~~~~~~~~~~~~~~~^^
File "/Users/erwin/curso-python/ventas.py", line 5, in registrar_venta
contador_de_ventas = contador_de_ventas + 1
^^^^^^^^^^^^^^^^^^
UnboundLocalError: cannot access local variable 'contador_de_ventas' where it is not associated with a valueSi borras la asignación y solo lees, funciona. Si asignas, es local. No hay término medio automático.
global, y por qué casi nunca#
Existe una palabra reservada para decirle a Python "no, este nombre no es local, es el de fuera":
contador_de_ventas = 0
def registrar_venta():
global contador_de_ventas
contador_de_ventas += 1
registrar_venta()
registrar_venta()
print(contador_de_ventas)2Funciona. Y aun así, la recomendación es no usarlo.
Qué hacer en su lugar#
Casi siempre lo mismo: lo que la función necesita, que entre por parámetro; lo que produce, que salga por return.
def registrar_venta(contador):
return contador + 1
contador_de_ventas = 0
contador_de_ventas = registrar_venta(contador_de_ventas)
contador_de_ventas = registrar_venta(contador_de_ventas)
print(contador_de_ventas)2Es una línea más larga en la llamada, pero ahora registrar_venta no depende de nada externo. Puedes leerla aislada y sabes exactamente qué hace.
El matiz importante: los objetos mutables#
Aquí es donde mucha gente se hace un lío, porque parece que contradice todo lo anterior. Si pasas una lista o un diccionario a una función, la función sí puede modificarlos, y el cambio se ve fuera:
def añadir_producto(carrito, producto):
carrito.append(producto)
compra = ["pan", "leche"]
añadir_producto(compra, "huevos")
print(compra)['pan', 'leche', 'huevos']No hay ninguna contradicción. Lo que se pasa a la función no es una copia de la lista, es el mismo objeto. El parámetro carrito y la variable compra son dos nombres apuntando a la misma lista en memoria, exactamente igual que cuando hacías b = a con listas y descubriste que modificar una afectaba a la otra. Al llamar a append, se modifica ese objeto compartido.
Con los diccionarios pasa lo mismo:
def anotar_stock(inventario, producto, unidades):
inventario[producto] = unidades
almacen = {"camiseta": 12}
anotar_stock(almacen, "zapatillas", 4)
print(almacen){'camiseta': 12, 'zapatillas': 4}Con un número, un texto o una tupla no ocurre, porque son inmutables: no hay ninguna operación que los cambie por dentro.
def subir_precio(precio):
precio = precio + 10
return precio
precio_base = 100
subir_precio(precio_base)
print(precio_base)
precio_base = subir_precio(precio_base)
print(precio_base)100
110La primera llamada no cambia nada fuera: dentro se crea un valor nuevo y se guarda en el nombre local precio. Solo cuando recogemos el return y reasignamos, cambia la variable de fuera.
Cómo busca Python un nombre#
| Dónde busca | Qué es |
|---|---|
| 1. Local | Las variables y parámetros de la función en curso |
| 2. Global | Las definidas en el nivel principal del fichero |
| 3. Incorporado | Lo que trae Python de serie: print, len, sum, list... |
Si no lo encuentra en ninguno de los tres, lanza el NameError.