QuieroProgramar por Rodri Gonzalez
Python · Lección 22 de 27

Scope de variables

Cuando escribís x = 10 dentro de una función, ¿es la misma x que la de afuera? No. Python tiene reglas estrictas sobre dónde "vive" cada variable. Entender el scope te evita bugs invisibles y te permite escribir funciones seguras.

60–80 min Prerrequisitos: 18-19 Funciones
01

Concepto teórico

¿Qué es el scope?

El scope (ámbito) de una variable es la zona del código donde esa variable es visible y accesible. Cuando usás un nombre (x, saldo, print), Python busca en un orden específico hasta encontrarlo. Si no lo encuentra en ningún nivel, da NameError.

La regla LEGB

Python busca nombres en este orden estricto (de más interno a más externo):

  1. L — Local: variables definidas DENTRO de la función actual
  2. E — Enclosing: variables de funciones externas (en funciones anidadas / closures)
  3. G — Global: variables definidas a nivel del módulo (fuera de toda función)
  4. B — Built-in: nombres integrados de Python (print, len, range, True)
Regla de oro: Python SIEMPRE busca de adentro hacia afuera: L → E → G → B. Usa la primera coincidencia que encuentra. Si definís print = 42 en tu código, sobreescribís el built-in print y ya no podés imprimir (¡no lo hagas!).

Scope local — variables dentro de funciones

Cada función crea su propio scope local. Las variables definidas dentro de una función solo existen dentro de esa función. Cuando la función termina, desaparecen:

def calcular():
    resultado = 42    # LOCAL a calcular()
    return resultado

calcular()
# print(resultado)  # NameError: resultado no existe acá afuera

Esto es una VENTAJA: cada función trabaja con sus propias variables sin interferir con el resto del programa. Es como oficinas privadas — lo que pasa adentro no afecta a las demás.

Scope global — variables del módulo

Las variables definidas fuera de toda función son globales. Se pueden LEER desde cualquier función, pero NO se pueden MODIFICAR sin declarar global:

IVA = 0.21  # global

def con_iva(precio):
    return precio * (1 + IVA)  # ✅ LEER global: funciona

def cambiar_iva():
    # IVA = 0.27  ← esto crea una LOCAL nueva, no modifica la global
    global IVA   # ← esto SÍ permite modificar la global
    IVA = 0.27
Evitá global siempre que puedas. Las variables globales mutables son una fuente de bugs porque cualquier función puede cambiarlas en cualquier momento. Es como tener una planilla de Excel compartida sin control de cambios. Preferí pasar valores como parámetros y devolver resultados con return.

Nonlocal — closures y funciones anidadas

nonlocal permite que una función interna modifique variables de la función externa (scope Enclosing). Se usa en closures con estado:

def crear_contador():
    cuenta = 0          # scope Enclosing
    def incrementar():
        nonlocal cuenta  # sin esto, Python crea una local nueva
        cuenta += 1
        return cuenta
    return incrementar

cont = crear_contador()
print(cont())  # 1
print(cont())  # 2 — recuerda el estado

Errores comunes de scope

Error Causa Solución
UnboundLocalError Asignás a una variable que Python ya interpretó como local Usá global o pasá como parámetro
NameError La variable no existe en ningún scope Verificá el nombre, ¿la definiste?
Sobreescribir built-in list = [1,2] oculta list() Nunca uses nombres de built-ins
Mutación accidental Modificar una lista global desde una función Trabajá con copias o pasá explícitamente
Analogía: el scope es como la organización de una empresa. Una variable local es una nota en tu escritorio (solo vos la ves). Una global es un memo en el pizarrón de la oficina (todos lo leen). global es como editar el pizarrón directamente. nonlocal es como editar el post-it de tu jefe directo. Lo ideal es que cada uno trabaje con sus propias notas y las comparta mediante reportes (return).
En el trabajo: el 90% de los bugs de scope se evitan con una regla simple: las funciones reciben datos por parámetro y devuelven resultados por return. No lean ni escriban globales directamente. Esto hace tu código testeable, predecible y libre de efectos secundarios.
02

Ejemplos explicados paso a paso

Ejemplo 1: LEGB en acción

ejemplo_01_legb.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 2: El error UnboundLocalError

ejemplo_02_error.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 3: nonlocal en closures con estado

ejemplo_03_nonlocal.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 4: Peligro de sobreescribir built-ins

ejemplo_04_builtins.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 5: Scope y mutabilidad — la trampa de las listas globales

ejemplo_05_mutabilidad.pyPython

        
Hacé clic en ▶ Ejecutar
03

Referencia rápida

Scope Dónde vive Leer Modificar
Local Dentro de la función Directo Directo
Enclosing Función externa (closure) Directo Con nonlocal
Global Nivel del módulo Directo Con global
Built-in Python interno Directo No (no lo hagas)
Buena práctica Mala práctica
Pasar datos como parámetros Leer variables globales dentro de funciones
Devolver resultados con return Modificar globales con global
Trabajar con copias de listas/dicts Mutar argumentos mutables silenciosamente
Usar nombres descriptivos Sobreescribir built-ins (list, dict, type)
04

Ejercicios

Nivel 1 · Básico

Ejercicio 1: Identificar scopes

Dado el código, predecí qué imprime ANTES de ejecutar. Después ejecutá para verificar. Debe incluir local.

ejercicio_01.pyDebe incluir "local"

          
Hacé clic en ▶ Ejecutar
Nivel 1 · Básico

Ejercicio 2: Leer global desde función

Definí IVA = 0.21 como global. Creá una función con_iva(precio) que la USE (sin global). Debe incluir 12100.

ejercicio_02.pyDebe incluir "12100"

          
Hacé clic en ▶ Ejecutar
Nivel 1 · Básico

Ejercicio 3: UnboundLocalError

Reproducí el error UnboundLocalError y después arreglalo pasando el valor como parámetro. Debe incluir 150000.

ejercicio_03.pyDebe incluir "150000"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 4: Global keyword

Creá un contador global operaciones = 0 y una función registrar() que lo incremente con global. Llamala 5 veces e imprimí. Debe incluir 5.

ejercicio_04.pyDebe incluir "5"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 5: Nonlocal en closure

Creá un closure crear_acumulador() que devuelva una función agregar(monto) que acumule un total con nonlocal. Debe incluir 300.

ejercicio_05.pyDebe incluir "300"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 6: Evitar sobreescribir built-ins

Corregí este código que sobreescribe list y sum. Después usá los verdaderos list() y sum(). Debe incluir 15.

ejercicio_06.pyDebe incluir "15"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 7: Función pura vs impura

Creá una versión "impura" (modifica global) y una "pura" (parámetro + return) de una función que aplique descuento a un precio. La pura debe devolver el resultado sin modificar nada. Debe incluir 8500.

ejercicio_07.pyDebe incluir "8500"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 8: Scope en comprehensions

Las variables de for en list comprehensions tienen scope local (Python 3+). Demostralo: creá x = "global", después resultado = [x for x in range(5)], e imprimí x para ver que sigue siendo "global". Debe incluir global.

ejercicio_08.pyDebe incluir "global"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 9: Closure con scope aislado

Creá una fábrica de funciones crear_filtro(umbral) que devuelva una función que filtre elementos mayores al umbral de una lista. Creá dos filtros con umbrales distintos. Debe incluir [890000].

ejercicio_09.pyDebe incluir "[890000]"

          
Hacé clic en ▶ Ejecutar
Nivel 4 · Desafío

Ejercicio 10: Mini sistema bancario con closures

Creá crear_cuenta(titular, saldo_inicial) que devuelva 3 funciones: depositar(monto), extraer(monto) y consultar(). Usá nonlocal para manejar el saldo. Probá con operaciones. Debe incluir Saldo:.

ejercicio_10_desafio.pyDebe incluir "Saldo:"

          
Hacé clic en ▶ Ejecutar
05

Resumen y conexión

En la siguiente lección (23 · Strings avanzado) vas a profundizar en slicing, regex con re, encoding UTF-8 y patrones avanzados de procesamiento de texto.

Recursos: Python docs — Scopes