Saltar al contenido
Editor

Ámbito de variables en Python, clausuras y la regla LEGB

Intermedio25 min5 ejercicios70 XP

Esta lección es una traducción del original en inglés.Read in EnglishAvisar de un error de traducción

0/5 ejercicios

Imagina una casa con varias habitaciones. Cada habitación tiene sus propios objetos: una lámpara en la mesita de noche del dormitorio, un cuchillo en el cajón de la cocina. Puedes usar lo que hay en la habitación en la que estás, y puedes salir al pasillo a buscar algo compartido. Pero no puedes meter la mano en la habitación cerrada de otra persona sin abrir antes la puerta.

Las variables de Python funcionan igual. Cada variable vive en un ámbito (scope): una región concreta de tu código donde esa variable existe y se puede usar. Entender el ámbito es la clave para escribir funciones limpias que no se pisen los datos unas a otras por accidente.

En este tutorial aprenderás los cuatro niveles de ámbito de Python (la regla LEGB), cómo usar las palabras clave global y nonlocal, y cómo las clausuras (closures) permiten que las funciones internas recuerden valores de la función que las contiene. Al terminar, podrás depurar con confianza los errores de "variable no encontrada" más confusos.

¿Qué es el ámbito local?

Cuando creas una variable dentro de una función, solo existe dentro de esa función. Esto se llama ámbito local. Es como una habitación privada: nada de fuera de la función puede ver ni tocar esa variable.

Las variables locales viven dentro de la función
def greet():
message = "Hello from inside!"
print(message)
greet()
# This would cause an error:
# print(message) # NameError: name 'message' is not defined
Salida
Hello from inside!

La variable message se crea dentro de greet(), así que es local a esa función. Cuando greet() termina, message desaparece. Si intentas usarla fuera, Python lanza un NameError.

Cada llamada a una función crea un conjunto nuevo de variables locales. No se conservan de una llamada a otra:

Las variables locales se reinician en cada llamada
def count_up():
x = 0
x = x + 1
print(f"x is {x}")
count_up() # x is 1
count_up() # x is 1 (starts fresh each time)
count_up() # x is 1
Salida
x is 1
x is 1
x is 1

¿Qué es el ámbito global?

Una variable creada fuera de cualquier función vive en el ámbito global. Es como un objeto en el pasillo de tu casa: todas las habitaciones pueden verlo.

Las variables globales se ven dentro de las funciones
language = "Python" # Global variable
def show_language():
print(f"Language: {language}")
def show_greeting():
print(f"Welcome to {language}!")
show_language()
show_greeting()
Salida
Language: Python
Welcome to Python!

Las dos funciones pueden leer la variable global language. Pero ¿qué pasa si intentas cambiarla dentro de una función?

No puedes modificar una variable global directamente
counter = 0
def increment():
counter = counter + 1 # This causes an error!
print(counter)
# Uncomment the line below to see the error:
# increment()
Salida
(sin salida)

Esto falla con un UnboundLocalError. Python ve counter = ... dentro de la función y supone que counter es una variable local. Pero luego el lado derecho intenta leer counter antes de que tenga un valor local. Python se confunde.

La regla LEGB: cómo encuentra Python las variables

Cuando usas el nombre de una variable, Python busca en cuatro lugares, en un orden concreto. Esto se conoce como la regla LEGB:

  • L - Local: las variables de la función actual.
  • E - Enclosing (envolvente): las variables de cualquier función externa, la que contiene a la actual (en funciones anidadas).
  • G - Global: las variables del nivel superior del módulo (el archivo).
  • B - Built-in (integrado): los nombres integrados de Python, como print, len, range.
  • Python los revisa en orden: primero L, luego E, luego G y por último B. Se detiene en la primera coincidencia. Si no encuentra nada en ninguno, obtienes un NameError.

    La regla LEGB en acción
    # B - Built-in: len is always available
    # G - Global scope
    x = "global x"
    def outer():
    # E - Enclosing scope
    x = "enclosing x"
    def inner():
    # L - Local scope
    x = "local x"
    print(x) # Finds "local x" first (L)
    inner()
    print(x) # Finds "enclosing x" (E is now L for outer)
    outer()
    print(x) # Finds "global x" (G)
    Salida
    local x
    enclosing x
    global x

    Cada print(x) encuentra una x distinta porque busca desde su propio ámbito hacia afuera. La función interna encuentra primero su x local y deja de buscar.

    La palabra clave global

    Si necesitas modificar una variable global desde dentro de una función, tienes que indicárselo a Python de forma explícita con la palabra clave global. Es como conseguir una llave especial para salir al pasillo y cambiar las cosas de sitio.

    Uso de la palabra clave global
    score = 0
    def add_points(points):
    global score
    score = score + points
    print(f"Score is now {score}")
    add_points(10)
    add_points(25)
    add_points(5)
    print(f"Final score: {score}")
    Salida
    Score is now 10
    Score is now 35
    Score is now 40
    Final score: 40

    La declaración global score le dice a Python: "No estoy creando una variable local nueva. Quiero usar la global". Ahora la asignación score = score + points funciona correctamente.

    Con global (más difícil de seguir)
    total = 0
    
    def add(n):
        global total
        total = total + n
    
    add(5)
    add(3)
    print(total)  # 8
    Con return (más limpio)
    def add(current, n):
        return current + n
    
    total = 0
    total = add(total, 5)
    total = add(total, 3)
    print(total)  # 8

    Ámbito envolvente y clausuras

    Cuando defines una función dentro de otra, la función interna puede acceder a las variables de la función externa. El ámbito de esa función externa se llama ámbito envolvente (enclosing scope): la "E" de LEGB.

    Una clausura recuerda la variable del ámbito envolvente
    def make_greeting(greeting):
    def greet(name):
    return f"{greeting}, {name}!"
    return greet
    hello = make_greeting("Hello")
    howdy = make_greeting("Howdy")
    print(hello("Alice"))
    print(hello("Bob"))
    print(howdy("Alice"))
    Salida
    Hello, Alice!
    Hello, Bob!
    Howdy, Alice!

    Aquí pasa algo notable. make_greeting("Hello") termina de ejecutarse y devuelve la función interna greet. Normalmente, la variable local greeting se destruiría. ¡Pero la función interna todavía la necesita!

    Python mantiene viva greeting porque greet hace referencia a ella. Esta combinación de una función y las variables que recuerda de su ámbito envolvente se llama clausura (closure).

    Las clausuras sirven para crear funciones especializadas. Aquí tienes un ejemplo práctico, una fábrica de multiplicadores:

    Una clausura como fábrica de funciones
    def make_multiplier(factor):
    def multiply(number):
    return number * factor
    return multiply
    double = make_multiplier(2)
    triple = make_multiplier(3)
    print(double(5)) # 10
    print(triple(5)) # 15
    print(double(100)) # 200
    Salida
    10
    15
    200

    La palabra clave nonlocal

    Igual que global te permite modificar variables globales, nonlocal te permite modificar variables del ámbito envolvente. Sin ella, asignar un valor a la variable crearía una variable local nueva.

    Uso de nonlocal para modificar variables del ámbito envolvente
    def make_counter():
    count = 0
    def increment():
    nonlocal count
    count = count + 1
    return count
    return increment
    counter = make_counter()
    print(counter()) # 1
    print(counter()) # 2
    print(counter()) # 3
    Salida
    1
    2
    3

    Sin nonlocal count, la línea count = count + 1 fallaría con un UnboundLocalError, el mismo problema que vimos con las globales. La palabra clave nonlocal le dice a Python que busque la variable un ámbito más afuera.

    Este patrón es muy potente. La variable count queda completamente oculta para el mundo exterior. Ningún código fuera de make_counter puede acceder a ella ni manipularla. Es una forma de encapsulamiento: mantener los datos privados.

    Errores de ámbito comunes y cómo corregirlos

    Estos son los errores de ámbito con los que más se tropieza al programar en Python. Conocer estos patrones te ahorrará horas de depuración.

    Error 1: ocultar una variable global

    La variable local oculta a la global
    items = ["apple", "banana", "cherry"]
    def process():
    items = [] # Creates a NEW local variable, doesn't touch the global!
    items.append("date")
    print(f"Inside: {items}")
    process()
    print(f"Outside: {items}") # Global is unchanged!
    Salida
    Inside: ['date']
    Outside: ['apple', 'banana', 'cherry']

    El items = [] de dentro de la función crea una variable local totalmente nueva que oculta a la global (en inglés se llama shadowing). La lista global items nunca se modifica. Esto suele ser un error.

    Error 2: UnboundLocalError por mezclar lectura y escritura

    UnboundLocalError explicado
    total = 100
    def update():
    # Python sees "total = ..." below and marks total as local
    # But this line tries to READ it before it's assigned locally
    print(total) # UnboundLocalError!
    total = 200
    # Uncomment to see the error:
    # update()
    Salida
    (sin salida)

    Python revisa todo el cuerpo de la función antes de ejecutarla. Si ve cualquier asignación a total, marca total como local en toda la función. Después, la línea print(total) intenta leer una variable local que todavía no tiene valor.


    Ejercicios de práctica

    Es hora de poner en práctica el ámbito y las clausuras. Estos ejercicios van desde leer el ámbito hasta construir tus propias clausuras.

    Ejercicio 1: predice la salida según el ámbito
    Predice la salida

    Lee este código con atención y predice lo que imprime. Después escribe una sola instrucción print() que muestre exactamente ese resultado.

    x = 10
    
    def foo():
        x = 20
        print(x)
    
    foo()
    print(x)

    Imprime los dos números en líneas separadas, exactamente como los mostraría el código de arriba.

    # Print the exact output of the code above
    Ejercicio 2: corrige el error del contador global
    Corrige el error

    Este código intenta incrementar un contador global, pero falla con un UnboundLocalError. Corrígelo para que la salida sea:

    1
    2
    3
    count = 0
    def increment():
    count = count + 1
    print(count)
    increment()
    increment()
    increment()
    Ejercicio 3: construye una clausura de saludo
    Escribe el código

    Escribe una función llamada make_greeter que reciba una cadena greeting como argumento y devuelva una función nueva. La función devuelta debe recibir un argumento name e imprimir el saludo con el nombre.

    Por ejemplo:

    hello = make_greeter("Hello")
    hello("World")

    Debe imprimir: Hello, World!

    La prueba hará estas llamadas:

    hi = make_greeter("Hi")
    hi("Alice")
    hi("Bob")

    Salida esperada:

    Hi, Alice!
    Hi, Bob!
    def make_greeter(greeting):
    # Return a function that prints greeting + name
    pass
    hi = make_greeter("Hi")
    hi("Alice")
    hi("Bob")
    Ejercicio 4: construye un contador con nonlocal
    Escribe el código

    Escribe una función llamada make_counter que reciba un valor start y devuelva una función. Cada vez que se llame a la función devuelta, debe imprimir la cuenta actual y luego aumentarla en 1.

    La prueba hará estas llamadas:

    c = make_counter(1)
    c()
    c()
    c()

    Salida esperada:

    1
    2
    3
    def make_counter(start):
    # Your code here
    pass
    c = make_counter(1)
    c()
    c()
    c()
    Ejercicio 5: construye una clausura de promedio acumulado
    Escribe el código

    Escribe una función llamada make_averager que devuelva una función. Cada vez que se llame a la función devuelta con un número, debe imprimir el promedio acumulado de todos los números recibidos hasta ese momento.

    El promedio acumulado es la suma de todos los números dividida entre la cantidad de números.

    La prueba hará estas llamadas:

    avg = make_averager()
    avg(10)
    avg(20)
    avg(30)

    Salida esperada:

    10.0
    15.0
    20.0

    Pista: la función necesita recordar tanto la suma total como la cantidad de números.

    def make_averager():
    # Your code here
    pass
    avg = make_averager()
    avg(10)
    avg(20)
    avg(30)

    Resumen

    Lo que ya sabes sobre el ámbito en Python:

    ConceptoQué significa
    Ámbito localVariables creadas dentro de una función; solo existen ahí
    Ámbito globalVariables creadas en el nivel del módulo; visibles en todas partes
    Regla LEGBPython busca en Local, Enclosing, Global y Built-in, en ese orden
    Palabra clave globalPermite que una función modifique una variable del nivel del módulo
    Ámbito envolventeLas variables de una función externa, accesibles desde las funciones internas
    ClausuraUna función que recuerda variables de su ámbito envolvente
    Palabra clave nonlocalPermite que una función interna modifique una variable del ámbito envolvente

    La regla de oro: prefiere pasar argumentos y devolver valores antes que usar global o nonlocal. Las clausuras son la única excepción en la que el ámbito envolvente brilla: te permiten crear funciones especializadas con estado privado.

    ¿Qué sigue?

    Ahora que entiendes el ámbito y las clausuras, puedes pasar a la [recursión](/python/python-recursion), donde las funciones se llaman a sí mismas. Entender el ámbito es esencial para la recursión, porque cada llamada recursiva tiene su propio ámbito local.

    Tutoriales relacionados

    ¿Te sirvió este tutorial?

    Sugerir una mejora