Funciones, scope y argumentos

Python · Lección 2

L2 Derivación Fase F0 Libro McKinney cap. 3 Prereqs Tipos y estructuras

La idea

Una función en Python es un objeto como cualquier otro: se guarda en variables, se pasa como argumento, se devuelve desde otra función. Eso es lo que hace posible df.apply(mi_funcion) y todo el estilo funcional de pandas.

Y hay una regla de resolución de nombres — LEGB — que decide de dónde sale cada variable que una función menciona.

LEGB: dónde busca Python un nombre

Cuando dentro de una función escribes x, Python busca en este orden y se detiene en el primero que encuentre:

Sigla Ámbito Qué es
L Local Dentro de la función actual
E Enclosing En la función que la contiene, si está anidada
G Global A nivel del módulo/archivo
B Built-in len, sum, print

Quita la línea x = "local" y vuelve a correr: interna ya no encuentra x localmente y sube al ámbito enclosing.

WarningEl error que todos cometen

Asignar a un nombre dentro de una función lo vuelve local para toda la función, incluso antes de la línea de asignación.

Python ve la asignación, marca contador como local, y al intentar leerlo del lado derecho todavía no existe. La solución no es global — es devolver el valor y reasignarlo fuera. Las funciones que mutan estado global son la fuente número uno de bugs irreproducibles en notebooks.

Argumentos: los cuatro tipos

  • Posicionales: sucursal, mes — obligatorios, por orden.
  • *extras: recoge los posicionales de más en una tupla.
  • Por defecto: moneda="USD" — opcional.
  • **opciones: recoge los nombrados de más en un diccionario.

La trampa del argumento mutable por defecto

Este es un clásico de entrevista, y algo que vas a ver en código real.

Por qué la lista se acumula entre llamadas

El def se ejecuta una sola vez, cuando Python lee el archivo — no en cada llamada. En ese momento evalúa las expresiones de los valores por defecto.

[] se evalúa entonces y produce un objeto lista. Ese objeto se guarda en el atributo __defaults__ de la función.

Cada llamada sin el argumento bolsa no crea una lista nueva: reutiliza ese mismo objeto guardado.

bolsa.append(item) es un método mutador sobre un objeto mutable (Lección 1): modifica el objeto compartido en su lugar. Por eso persiste entre llamadas.

Compruébalo:

El patrón correcto usa None como centinela:

Nota que aquí sí se usa is None y no == None — exactamente la regla de la Lección 1.

Funciones como objetos

lambda es solo una función anónima de una expresión. Úsala para funciones de una línea que se pasan como argumento; para cualquier otra cosa, def con nombre es más legible.

Ejercicio 1 — Arreglar el default mutable

Corrige la función para que cada llamada sin registro arranque con un diccionario vacío.

Usa None como valor por defecto y, en la primera línea del cuerpo, crea el diccionario si el argumento llegó como None. Recuerda comparar con is, no con ==.

Ejercicio 2 — Sin estado global

Reescribe esta función para que no dependa de una variable global: debe recibir el total actual y devolver el nuevo.

La función debe recibir dos parámetros (el acumulado y el nuevo valor) y devolver su suma. No debe asignar a ninguna variable de fuera.

Reto

En proyectos/notebooks/F0-retos.ipynb:

  1. Escribe un decorador @cronometrar que imprima cuánto tardó la función que envuelve. Necesitarás time.perf_counter y functools.wraps.
  2. Escribe componer(f, g) que devuelva una función nueva equivalente a f(g(x)). Verifica que componer(doblar, incrementar)(5) da 12.
  3. Explica, en dos líneas, por qué sorted(lista, key=lambda x: x[1]) ordena por el segundo elemento. ¿Qué recibe key y qué hace sorted con lo que devuelve?

Para el Cerebro

Lee McKinney cap. 3 (secciones de funciones) con estas preguntas escritas antes:

  1. ¿Qué devuelve una función que no tiene return?
  2. ¿Puede una función devolver varios valores? ¿Qué está pasando realmente?
  3. ¿Qué es un closure y por qué una función anidada puede leer variables de la de afuera después de que esta terminó?
¿Qué significa LEGB?::Local, Enclosing, Global, Built-in — el orden en que Python resuelve un nombre
¿Cuándo se evalúan los valores por defecto de una función?::Una sola vez, al definir la función, no en cada llamada
¿Cuál es el patrón correcto para un default mutable?::Usar None como centinela y crear el objeto dentro del cuerpo
¿Qué recoge *args y qué recoge **kwargs?::*args los posicionales sobrantes en una tupla; **kwargs los nombrados en un dict
¿Por qué asignar a un nombre dentro de una función puede dar UnboundLocalError?::La asignación lo marca local para toda la función, incluso antes de esa línea

Fuentes

Lo que esta página demuestra sola. Los ejercicios corren en tu navegador.

Lo que viene de los libros.

Índices verificados el 11-09-2026 contra el índice publicado de cada libro.

→ Siguiente: Comprehensions e iteradores