Lección 4 · 4 de 20
Clausuras y nonlocal · Python
Funciones que devuelven funciones y recuerdan sus variables: clausuras en Python y nonlocal para cambiarlas, con descuentos y turnos.

En Python una función puede definir otra dentro y devolverla. make_discount(0.20) crea la función apply y la devuelve sin llamarla. Lo curioso es que apply sigue viendo rate aunque make_discount ya haya terminado: eso es una clausura, una función que lleva consigo las variables del sitio donde nació.
Cada llamada crea una clausura distinta: sales recuerda 0.20 y members recuerda 0.35. Son como pequeñas fábricas de funciones configuradas.
Leer una variable de fuera no necesita nada especial, pero cambiarla sí. En next_ticket, number += 1 haría que Python tratara number como local (y fallaría con UnboundLocalError). La declaración `nonlocal number` indica que es la variable de la función de fuera, la que se conserva entre llamadas.
Por eso bakery y butcher llevan cuentas separadas: cada dispensador tiene su propio number. La última línea curiosea dentro: sales.__closure__[0].cell_contents es la celda donde la clausura guarda rate.
Las clausuras son la base de los decoradores (la próxima lección) y una alternativa ligera a una clase cuando solo hace falta guardar un dato y un método.
Código · main.py
def make_discount(rate): """Returns a function that remembers rate.""" def apply(price): return round(price * (1 - rate), 2) return applydef make_ticket_dispenser(prefix): """Each call hands out the next ticket number.""" number = 0 def next_ticket(): nonlocal number # change the outer variable number += 1 return f"{prefix}-{number:03d}" return next_ticketsales = make_discount(0.20)members = make_discount(0.35)print("Sales price:", sales(50.0))print("Members price:", members(50.0))bakery = make_ticket_dispenser("B")butcher = make_ticket_dispenser("M")print(bakery(), bakery(), bakery())print(butcher(), butcher())print("Next bakery ticket:", bakery())# The remembered values live in the closure cellsprint("Captured rate:", sales.__closure__[0].cell_contents)Salida · python3 main.py
Sales price: 40.0 Members price: 32.5 B-001 B-002 B-003 M-001 M-002 Next bakery ticket: B-004 Captured rate: 0.2
Para recordar
- Una función interior puede usar las variables de la exterior.
- Esas variables siguen vivas después de que la exterior termine.
nonlocalpermite cambiar una variable de la función de fuera.- Cada llamada a la fábrica crea una clausura independiente.
Ejercicio
Escriba make_counter(limit) que devuelva una función que cuente 1, 2, 3… y, al pasar de limit, devuelva "full".