Espacio de nombres y ámbito de las funciones
Al finalizar este tema
Podrás explicar el orden en que Python busca los nombres de las variables (regla LEGB) y manejar correctamente las situaciones en las que entran en conflicto las variables locales y globales.
Mismo nombre, diferente valor
x = 10
def foo(): x = 20 print(x)
foo() # 20print(x) # 10La variable x dentro de la función y la variable x fuera de ella son variables diferentes, aunque tengan el mismo nombre. Para comprender esto, es necesario entender los conceptos de espacio de nombres y ámbito.
¿Qué es un espacio de nombres?
Un espacio de nombres es un espacio que contiene la correspondencia entre nombres y objetos. En Python, funciona como un diccionario con la estructura {nombre: objeto}.
x = 10name = "Carlos"# Espacio de nombres del módulo: {"x": 10, "name": "Carlos", ...}def greet(): msg = "Hola" # Espacio de nombres de la función: {"msg": "Hola"}Al llamar a una función, se crea un nuevo espacio de nombres que desaparece al finalizar la función. Por lo tanto, las variables creadas dentro de la función no se pueden usar fuera de ella.
Ámbito y la regla LEGB
El ámbito es el rango en el código donde un nombre es visible. Python busca las variables siguiendo el orden LEGB:
L — Local Dentro de la función
E — Enclosing Función envolvente en funciones anidadas
G — Global Nivel superior del módulo
B — Built-in Integrado en Python (print, len, ...)x = "global" # G
def outer(): x = "enclosing" # E
def inner(): x = "local" # L print(x)
inner()
outer() # "local": se encuentra en L y deja de buscarComienza la búsqueda en el nivel más interno (L) y avanza hacia los niveles exteriores si no se encuentra. Se detiene tan pronto como se encuentra el elemento.
Local: variables dentro de la función
def calculate(): result = 42 # Variable local temp = result * 2 return temp
calculate()print(result) # NameError: name 'result' is not definedLas variables asignadas dentro de una función (=) se convierten automáticamente en variables locales. No existen fuera de la función.
Global: variables de nivel superior del módulo.
counter = 0 # Global
def increment(): print(counter) # ✅ Puede leerse: LEGB lo encuentra en G
def reset(): counter = 0 # ⚠️ Crea una variable local nueva, independiente de la global
reset()print(counter) # Sigue siendo 0: la global no cambióDentro de una función, se puede acceder a las variables globales sin restricciones. Sin embargo, si se intenta asignar un valor a una variable global dentro de la función, Python la tratará como una nueva variable local.
Palabra clave global
Para modificar una variable global dentro de una función, se utiliza la palabra clave global:
counter = 0
def increment(): global counter counter += 1
increment()increment()print(counter) # 2global counter es una declaración que indica que "counter dentro de esta función no es una variable local, sino global".
Sin embargo, el uso excesivo de global dificulta el seguimiento de los cambios en los valores. Siempre que sea posible, es mejor pasar datos a través de los parámetros y el valor de retorno de la función.
Enclosing: funciones anidadas y nonlocal
def outer(): count = 0
def inner(): nonlocal count count += 1 return count
print(inner()) # 1 print(inner()) # 2
outer()nonlocal es una declaración que indica que "esta variable no es local, sino que pertenece a la función que la contiene". Se utiliza al crear closures.
Error común — UnboundLocalError
x = 10
def broken(): print(x) # ❌ UnboundLocalError! x = 20
broken()Se produce un error. Python analiza toda la función de antemano y, si encuentra x = 20, determina que x es una variable local. Sin embargo, en el momento de print(x), la variable local x aún no tiene ningún valor asignado, por lo que se produce un error.
Solución:
def fixed_1(): x = 20 # Asignación previa print(x)
def fixed_2(): global x # Declara explícitamente que es global print(x) x = 20Integrado — Nombres integrados de Python
print(len([1, 2, 3])) # 3: len es built-in
# Si se sobrescribe accidentalmente un nombre built-inlist = [1, 2, 3] # ⚠️ Sobrescribe el built-in listnew_list = list("abc") # TypeError!Los nombres como list, dict, print y len pertenecen al espacio de nombres predeterminado. Si se utilizan estos nombres como variables, se ocultará el espacio de nombres predeterminado y no se podrá acceder a su funcionalidad original.
# Recuperacióndel list # Al eliminar list local/global vuelve a quedar visible el built-inClosures: Aplicaciones prácticas de Enclosing
def make_multiplier(factor): def multiply(x): return x * factor return multiply
double = make_multiplier(2)triple = make_multiplier(3)
print(double(5)) # 10print(triple(5)) # 15La función multiply conserva el valor de factor incluso después de su ejecución. Esto se conoce como clausura (closure): la capacidad de una función de acceder y utilizar las variables del ámbito en el que fue definida, incluso después de que ese ámbito haya finalizado.
def make_counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment
counter = make_counter()print(counter()) # 1print(counter()) # 2print(counter()) # 3También se pueden crear funciones que mantengan el estado sin necesidad de definir una clase.
Depuración de espacios de nombres
def debug_scope(): x = 10 y = 20 print(locals()) # {'x': 10, 'y': 20}
debug_scope()print(globals().keys()) # Todos los nombres del espacio globallocals() y globals() devuelven el espacio de nombres del ámbito actual como un diccionario. Esto es útil para determinar a qué ámbito pertenece una variable.
import builtinsprint(dir(builtins)) # Todos los nombres del espacio built-inResumen clave
| Palabra clave | Significado |
|---|---|
| (ninguna) | Asignación → ámbito local, referencia → búsqueda en el orden LEGB |
global | "Esta variable pertenece al ámbito global" |
nonlocal | "Esta variable pertenece a la función contenedora" |
Orden de búsqueda: Local → Enclosing → Global → Built-in
Regla de asignación: usar = dentro de una función se considera LocalMódulos y espacios de nombres
import mathfrom os import path
# import separa el espacio de nombres mediante el nombre del móduloprint(math.pi) # pi del espacio de nombres mathprint(path.exists) # exists de os.path
# from import * contamina el espacio de nombresfrom math import * # ⚠️ Vuelca todos los nombres en el espacio global actualprint(pi) # Funciona, pero su origen no queda claroimport math agrega solo un nombre, math, al espacio de nombres global. Por otro lado, from math import * agrega decenas de nombres al espacio de nombres global, lo que aumenta el riesgo de conflictos de nombres.
Decoradores y ámbitos
def log_calls(func): count = 0 def wrapper(*args, **kwargs): nonlocal count count += 1 print(f"[Llamada {count}] {func.__name__}") return func(*args, **kwargs) return wrapper
@log_callsdef greet(name): return f"Hola, {name}"
greet("Carlos") # [Llamada 1] greetgreet("Ana") # [Llamada 2] greetLos decoradores son una aplicación práctica de los cierres (closures). count reside en el Local (= wrapper de log_calls) y aumenta con nonlocal en cada llamada.
Los errores causados por colisiones de espacios de nombres son difíciles de depurar. Se pueden evitar estos errores si siempre se tiene presente "a qué pertenece esta variable".