Funciones, Argumentos y Ámbito
Las funciones agrupan lógica reutilizable con una vinculación de parámetros flexible. Las reglas de ámbito LEGB de Python, los argumentos variádicos *args/**kwargs y los parámetros solo por palabra clave te permiten diseñar APIs que son tanto expresivas como seguras.
Receta
def greet(name: str, *, excited: bool = False) -> str:
msg = f"Hello, {name}"
return msg + "!" if excited else msg
def connect(host: str, port: int = 443, **options: object) -> dict:
return {"host": host, "port": port, **options}
def total(*amounts: float, tax: float = 0.0) -> float:
return sum(amounts) * (1 + tax)Cuándo usar esto:
- Diseñar APIs de bibliotecas con configuración opcional
- Envolver llamables con decoradores (ver sección de iteradores)
- Implementar cierres (closures) que recuerdan estado
- Evitar estado global mutable en módulos
Ejemplo de Trabajo
from collections.abc import Callable
def make_multiplier(factor: int) -> Callable[[int], int]:
def multiply(value: int) -> int:
return value * factor
return multiply
def append_item(item: object, bucket: list | None = None) -> list:
if bucket is None:
bucket = []
bucket.append(item)
return bucket
def call_with_retry(fn: Callable[[], object], retries: int = 3) -> object:
last_error: Exception | None = None
for attempt in range(retries):
try:
return fn()
except Exception as exc:
last_error = exc
raise RuntimeError("failed") from last_error
counter = 0
def increment_global() -> int:
global counter
counter += 1
return counter
def make_counter() -> Callable[[], int]:
count = 0
def inc() -> int:
nonlocal count
count += 1
return count
return inc
if __name__ == "__main__":
double = make_multiplier(2)
print(double(21))
print(append_item("a"))
print(append_item("b")) # lista nueva - sin valor predeterminado compartido
inc = make_counter()
print(inc(), inc())Lo que esto demuestra:
- El cierre (closure) captura
factordel ámbito envolvente - El centinela
Noneevita el error del valor predeterminado de lista mutable nonlocalactualizacountdentro de la función anidadaglobalmodifica explícitamentecountera nivel de módulo
Inmersión Profunda
Cómo Funciona
- LEGB - Local (función), Enclosing (funciones externas anidadas), Global (módulo), Built-in (integrado:
len,print). - Tipos de parámetros - Posicionales, de palabra clave, solo posicionales (
/), solo de palabra clave (*). *args- Tupla de argumentos posicionales adicionales.**kwargs- Diccionario de argumentos de palabra clave adicionales.- Anotaciones - Se almacenan en
__annotations__; se evalúan en el momento de la definición de la función.
Orden de Parámetros
def f(pos_only, /, standard, *, kw_only, **kwargs): ...| Segmento | Vinculación |
|---|---|
Antes de / | Solo posicional |
Después de / | Posicional o de palabra clave |
Después de * | Solo de palabra clave |
Notas de Python
# Desempaquetado en el sitio de llamada
args = (1, 2)
kwargs = {"excited": True}
greet("Ada", **kwargs)
# functools.partial retrasa la vinculación de argumentos
from functools import partial
double = partial(make_multiplier, 2)Errores Comunes
- Valores predeterminados mutables -
def f(x=[])comparte una lista entre llamadas. Solución: Valor predeterminadoNoney asignación interna. - Cierres (closures) de vinculación tardía -
funcs = [lambda: i for i in range(3)]captura la variable, no el valor. Solución: Argumento predeterminadolambda i=i: i. - Sobrescritura de integrados (builtins) -
def len(x):rompelen(). Solución: Renombrar parámetros. globalpara estado de módulo - Dificulta las pruebas. Solución: Preferir clases u objetos de configuración explícitos.- Demasiados
*args/**kwargs- Oculta la superficie de la API. Solución: Nombrar explícitamente los parámetros importantes.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
dataclass | Paquetes de campos con estado | Transformación pura simple |
functools.partial | Aplicación parcial | Necesidad de aplicar restricciones de solo palabra clave |
Protocolo Callable | Tipado estructural para callbacks | Lambda simple de un solo uso |
Clase con __call__ | Objetos llamables con estado | Funciones sin estado |
Preguntas Frecuentes
¿Qué es LEGB?
Orden de búsqueda de nombres: Local, Enclosing (anidado en funciones externas), Global (módulo), espacio de nombres Built-in (integrado).
¿Cuándo uso * en la lista de parámetros?
Para marcar los parámetros siguientes como solo de palabra clave: def f(a, *, b=1) fuerza f(1, b=2).
¿Qué significa / en Python 3.8+?
Separa los parámetros solo posicionales: def f(a, b, /, c) - a y b no se pueden pasar por palabra clave.
¿Cómo funcionan *args y **kwargs juntos?
*args recopila argumentos posicionales extra como una tupla; **kwargs recopila argumentos de palabra clave extra como un diccionario. El orden en la firma importa.
¿Se evalúan los valores predeterminados una sola vez?
Sí, en el momento de la definición de la función; por eso los valores predeterminados mutables se comparten. Los valores predeterminados inmutables (int, str, tuple) son seguros.
¿Cuándo debería usar nonlocal?
Cuando una función anidada debe reasignar una variable en una función envolvente: contadores, acumuladores en cierres (closures).
¿Puedo anotar *args?
Sí: def f(*args: int) o *args: int con patrones TypedDict en tipado avanzado; consulta la sección de sugerencias de tipo.
¿Cuál es la limitación de lambda?
Solo una expresión; sin sentencias. Usa def para cualquier cosa no trivial.
¿Cómo reenvío todos los argumentos?
def wrapper(*args, **kwargs): return inner(*args, **kwargs) - común en decoradores.
¿Necesita anotación el tipo de retorno None?
-> None documenta que no hay retorno útil. Omítelo solo cuando sea obvio; mypy se beneficia de None explícito.
Relacionado
- Cierres y Captura de Ámbito - variables libres y fábricas
- Escribir Decoradores - envolver funciones
- functools - partial y wraps
- Conceptos Básicos de Sugerencias de Tipo - anotación de firmas
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.