Saltar al contenido
🐍 Curso de Python
/

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.

⏱ 13 min de lectura

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#

python
def calcular_factura():
        subtotal = 100
    print(f"Dentro: {subtotal}")


calcular_factura()
print(f"Fuera: {subtotal}")
Salida en pantalla
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 defined

subtotal 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:

python
IVA = 0.21


def calcular_precio_final(precio):
        return precio * (1 + IVA)


print(calcular_precio_final(100))
Salida en pantalla
121.0

Cuando 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:

python
contador_de_ventas = 0


def registrar_venta():
        contador_de_ventas = contador_de_ventas + 1
    print(contador_de_ventas)


registrar_venta()
Salida en pantalla
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 value

Si 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":

python
contador_de_ventas = 0


def registrar_venta():
        global contador_de_ventas
    contador_de_ventas += 1


registrar_venta()
registrar_venta()
print(contador_de_ventas)
Salida en pantalla
2

Funciona. 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.

python
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)
Salida en pantalla
2

Es 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:

python
def añadir_producto(carrito, producto):
        carrito.append(producto)


compra = ["pan", "leche"]
añadir_producto(compra, "huevos")
print(compra)
Salida en pantalla
['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:

python
def anotar_stock(inventario, producto, unidades):
        inventario[producto] = unidades


almacen = {"camiseta": 12}
anotar_stock(almacen, "zapatillas", 4)
print(almacen)
Salida en pantalla
{'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.

python
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)
Salida en pantalla
100
110

La 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 buscaQué es
1. LocalLas variables y parámetros de la función en curso
2. GlobalLas definidas en el nivel principal del fichero
3. IncorporadoLo que trae Python de serie: print, len, sum, list...

Si no lo encuentra en ninguno de los tres, lanza el NameError.