Cuando Python ejecuta una asignación como x = 10 dentro de una función, asume por defecto que x es una variable local a esa función. No busca en scopes externos para actualizarla; crea una nueva. Ese comportamiento es deliberado: evita que las funciones modifiquen silenciosamente el estado del entorno que las rodea. Pero a veces sí necesitas escribir en un scope exterior, y para eso existen global y nonlocal.
La regla de resolución de nombres en Python se llama LEGB (Local → Enclosing → Global → Built-in). Leer una variable sigue esa cadena automáticamente. Escribir en ella, no: sin una declaración explícita, cualquier asignación crea una variable local nueva, aunque exista otra con el mismo nombre en un scope exterior. global y nonlocal son las dos formas de romper esa restricción de escritura de manera intencional.
global x le dice al compilador de bytecode que, dentro de esta función, x no es local: todas sus lecturas y escrituras apuntan al módulo actual (el scope global). nonlocal x hace lo mismo pero hacia el scope enclosing más cercano —la función que contiene a la actual— sin llegar al nivel de módulo. nonlocal solo tiene sentido en funciones anidadas; usarlo en el nivel superior es un error de sintaxis.
El problema real con global no es técnico, es de diseño. Una función que depende de estado global tiene dependencias implícitas: no puedes leerla de forma aislada, no puedes testearla sin preparar el módulo entero, y dos llamadas a la misma función pueden producir resultados distintos dependiendo de qué haya pasado antes en el programa. Eso es exactamente lo que una función debería evitar. El patrón correcto es pasar datos como argumentos y retornar resultados. Cuando sientes la necesidad de escribir global, es casi siempre una señal de que falta extraer estado hacia una clase o rediseñar el flujo de datos.
Dicho esto, hay casos donde global es la herramienta adecuada: contadores de módulo que acumulan eventos a lo largo de toda la vida del proceso, o flags de configuración que se establecen una sola vez al inicio (lectura de variables de entorno, modo debug). nonlocal tiene su lugar natural en closures que necesitan mantener estado acumulado entre llamadas.
# global y nonlocal: casos reales
# ── 1. Contador de módulo con global ──────────────────────────────────────
_request_count = 0 # Convención: prefijo _ indica "privado al módulo"
def record_request() -> None:
global _request_count
_request_count += 1 # Sin 'global', esto lanzaría UnboundLocalError
def get_request_count() -> int:
return _request_count # Solo lectura: no necesita declaración global
# ── 2. Closure con estado acumulado usando nonlocal ───────────────────────
def make_running_average() -> callable:
total = 0.0
count = 0
def update(value: float) -> float:
nonlocal total, count # Necesitamos escribir en el scope enclosing
total += value
count += 1
return total / count
return update
# ── 3. Patrón incorrecto vs. correcto con global ──────────────────────────
# ❌ Lógica de negocio que depende de global: difícil de testear
_discount = 0.0
def apply_discount_bad(price: float) -> float:
return price * (1 - _discount) # Depende de estado global implícito
# ✅ La misma lógica como función pura: predecible y testeable
def apply_discount(price: float, discount: float) -> float:
return price * (1 - discount)
# ── Demo ──────────────────────────────────────────────────────────────────
if __name__ == "__main__":
# Contador de módulo
record_request()
record_request()
record_request()
print(f"Peticiones registradas: {get_request_count()}") # 3
# Closure con nonlocal
avg = make_running_average()
print(avg(10)) # 10.0
print(avg(20)) # 15.0
print(avg(30)) # 20.0
# Función pura vs. global
print(apply_discount(100.0, 0.15)) # 85.0 — resultado predecible
Desglose del código
El contador _request_count ilustra el caso de uso legítimo de global. Fíjate en que la función get_request_count lee la variable sin declaración especial —LEGB resuelve la lectura automáticamente hacia el scope global—, pero record_request necesita global _request_count porque ejecuta +=, que es una lectura seguida de una escritura. Sin esa declaración, Python vería la asignación y marcaría _request_count como local, intentaría leerla antes de inicializarla, y lanzaría UnboundLocalError. Ese error es exactamente lo que el ejemplo del bloque de errores más abajo muestra.
El closure make_running_average demuestra nonlocal en su territorio natural. Cada llamada a update necesita modificar total y count, que viven en el frame de make_running_average. Sin nonlocal, esas asignaciones crearían variables locales nuevas dentro de update, y el estado no se acumularía entre llamadas. La clave es que nonlocal no eleva las variables al módulo: siguen encapsuladas dentro de make_running_average, lo que es precisamente lo que queremos. El estado es privado al closure.
La comparación entre apply_discount_bad y apply_discount no es sobre rendimiento ni sobre sintaxis: es sobre razonabilidad. apply_discount(100.0, 0.15) siempre devuelve 85.0. No importa en qué momento del programa la llames, no importa qué haya ocurrido antes. apply_discount_bad(100.0) puede devolver cualquier cosa dependiendo del valor actual de _discount. Esa imprevisibilidad es el problema real, no el uso de global en sí.
Errores que debes conocer
Error: Intentar modificar una variable del scope enclosing sin nonlocal, lo que hace que Python la trate como local y lance UnboundLocalError al intentar leerla antes de asignarla.
# ❌ Wrong
def make_counter():
count = 0
def increment():
count += 1 # Python ve la asignación y marca count como local
return count
return increment
c = make_counter()
c() # UnboundLocalError: local variable 'count' referenced before assignment
# ✅ Right
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
nonlocal count le indica a Python que count pertenece al scope de make_counter, así la asignación actualiza esa variable en lugar de crear una local nueva.
Error: Declarar global para una variable que en realidad solo necesitas leer, lo que no rompe nada pero revela una confusión sobre cuándo es necesaria la declaración.
# ❌ Wrong
CONFIG = {"debug": False}
def is_debug():
global CONFIG # Innecesario: no estás escribiendo en CONFIG
return CONFIG["debug"]
# ✅ Right
def is_debug():
return CONFIG["debug"] # LEGB resuelve la lectura sin declaración especial
global solo es obligatorio cuando vas a asignar a la variable en el scope de módulo; leerla funciona automáticamente a través de la cadena LEGB.
Error: Usar global para compartir estado entre funciones de negocio cuando la solución correcta es una clase o pasar el estado como argumento.
# ❌ Wrong
_items = []
def add_item(item):
global _items
_items.append(item)
def get_items():
global _items # Innecesario aquí, pero el diseño completo es frágil
return _items
# ✅ Right
class Cart:
def __init__(self):
self._items = []
def add_item(self, item):
self._items.append(item)
def get_items(self):
return list(self._items)
Encapsular el estado en una clase hace que las dependencias sean explícitas, que el estado sea instanciable (puedes tener múltiples Cart), y que el código sea testeable sin efectos secundarios entre pruebas.
N° 60