La estructura match-case, disponible desde Python 3.10, permite comparar un valor con patrones estructurales. No es simplemente una versión de switch: además de comparar constantes, puede descomponer listas, tuplas, diccionarios y objetos, capturar partes del valor y aplicar condiciones adicionales. Utilizada correctamente, hace más legible el código que interpreta comandos, respuestas de APIs, eventos o estructuras de datos.
Antes de empezar conviene dominar las condiciones if, elif y else, las tuplas en Python, los conceptos de listas y las dataclasses. match-case resulta más útil cuando la forma de los datos importa tanto como su valor.
Sintaxis básica
def describir_estado(estado):
match estado:
case "pendiente":
return "La tarea todavía no comenzó"
case "procesando":
return "La tarea está en curso"
case "completado":
return "La tarea terminó"
case _:
return "Estado desconocido"El guion bajo representa cualquier valor que no coincidió anteriormente. Se recomienda colocarlo al final.
Comparar varios valores
El operador | permite agrupar alternativas:
def es_fin_de_semana(dia):
match dia.lower():
case "sábado" | "sabado" | "domingo":
return True
case _:
return FalseEsto evita repetir el mismo bloque para varios valores equivalentes.
Capturar el valor
def procesar_comando(comando):
match comando:
case "salir":
return "Finalizando"
case otro:
return f"Comando recibido: {otro}"Un nombre sin calificar captura cualquier valor. No se interpreta como una constante. Este detalle causa errores cuando un desarrollador espera comparar con una variable existente.
Patrones con secuencias
def interpretar_punto(punto):
match punto:
case [0, 0]:
return "Origen"
case [x, 0]:
return f"Eje X en {x}"
case [0, y]:
return f"Eje Y en {y}"
case [x, y]:
return f"Punto ({x}, {y})"
case _:
return "Formato inválido"El patrón acepta listas y tuplas compatibles con la longitud esperada. Las variables x e y reciben los elementos.
Capturar el resto de una secuencia
def analizar_ruta(partes):
match partes:
case ["usuarios", id_usuario]:
return ("perfil", id_usuario)
case ["usuarios", id_usuario, "pedidos", *resto]:
return ("pedidos", id_usuario, resto)
case _:
return None*resto captura los elementos restantes. Solo puede existir una captura con asterisco dentro del mismo patrón de secuencia.
Patrones con diccionarios
def manejar_evento(evento):
match evento:
case {"tipo": "login", "usuario": usuario}:
return f"Inicio de sesión: {usuario}"
case {"tipo": "compra", "id": id_compra, "total": total}:
return f"Compra {id_compra}: {total}"
case {"tipo": tipo}:
return f"Evento no tratado: {tipo}"
case _:
return "Evento inválido"El diccionario puede contener claves adicionales. El patrón exige únicamente las claves indicadas. Esto es útil para respuestas JSON de APIs.
Capturar claves adicionales
match evento:
case {"tipo": "compra", "id": identificador, **extras}:
print(identificador, extras)**extras guarda las demás claves. Evita confiar en ellas sin validarlas.
Usar guards con if
Un guard añade una condición después de que el patrón coincide:
def clasificar_pago(pago):
match pago:
case {"moneda": "EUR", "total": total} if total > 1000:
return "Revisión manual"
case {"moneda": "EUR", "total": total} if total >= 0:
return "Pago normal"
case _:
return "Pago inválido"El guard se evalúa únicamente después de capturar total. Si es falso, Python continúa con el siguiente case.
Patrones de clase
from dataclasses import dataclass
@dataclass
class Punto:
x: int
y: int
def describir(punto):
match punto:
case Punto(0, 0):
return "Origen"
case Punto(x, 0):
return f"Eje X: {x}"
case Punto(x, y):
return f"Punto: {x}, {y}"
case _:
return "No es un punto"Las dataclasses definen automáticamente información que permite patrones posicionales. También puedes utilizar nombres:
case Punto(x=0, y=valor):
print(valor)Comparar constantes correctamente
Un nombre simple captura, no compara. Para constantes utiliza literales, enumeraciones o atributos calificados:
from enum import Enum
class Estado(Enum):
ACTIVO = "activo"
INACTIVO = "inactivo"
match estado:
case Estado.ACTIVO:
print("Activo")
case Estado.INACTIVO:
print("Inactivo")Escribir case ACTIVO: capturaría el valor y podría hacer inalcanzables los casos siguientes.
Patrón as
as permite capturar el valor completo además de sus partes:
match respuesta:
case {"estado": "ok", "datos": [primero, *resto]} as completa:
print(primero, resto, completa)Esto es útil para registrar la estructura completa después de validarla.
Ejemplo: comandos de terminal
def ejecutar(tokens):
match tokens:
case ["crear", nombre]:
return crear(nombre)
case ["eliminar", nombre, "--forzar"]:
return eliminar(nombre, forzar=True)
case ["listar"]:
return listar()
case ["buscar", *terminos] if terminos:
return buscar(" ".join(terminos))
case _:
return "Uso inválido"El patrón expresa claramente las formas aceptadas de la entrada.
Ejemplo: procesar respuestas de una API
def interpretar_respuesta(respuesta):
match respuesta:
case {"ok": True, "data": datos}:
return datos
case {"ok": False, "error": {"code": codigo, "message": mensaje}}:
raise RuntimeError(f"{codigo}: {mensaje}")
case _:
raise ValueError("Respuesta inesperada")Aunque el patrón valida la estructura mínima, todavía debes verificar tipos, rangos y contenido.
Orden de los casos
Los casos se prueban de arriba hacia abajo. Coloca los patrones específicos antes de los generales:
match valor:
case [0, 0]:
...
case [x, y]:
...
case _:
...Si el patrón general aparece primero, los casos específicos nunca se ejecutan.
match-case vs if-elif
Utiliza if-elif cuando comparas condiciones independientes, rangos o expresiones booleanas:
if edad < 0:
...
elif edad < 18:
...Utiliza match-case cuando quieres describir formas de datos, descomponer estructuras o manejar varias variantes bien definidas. No reemplaces todas las condiciones solo porque la sintaxis es nueva.
Compatibilidad de versiones
match-case requiere Python 3.10 o superior. Comprueba la versión:
python --versionSi el proyecto debe funcionar en Python 3.9, utiliza if-elif, diccionarios de funciones u otras estrategias.
Documentación y especificación
La documentación oficial sobre sentencias match incluye ejemplos con secuencias, clases y guards. La especificación formal está en la PEP 634 sobre structural pattern matching.
Pruebas recomendadas
Prueba cada forma aceptada y varios valores inválidos. Incluye diccionarios sin claves, secuencias con longitudes incorrectas, tipos inesperados, guards falsos y casos generales. Las pruebas ayudan a detectar cambios cuando una API añade nuevas variantes.
Errores frecuentes
Los problemas más comunes son confundir una captura con una constante, colocar un patrón general antes de uno específico, usar match-case en una versión antigua, asumir que un patrón de diccionario rechaza claves adicionales y olvidar validar los tipos capturados. También debes evitar bloques demasiado grandes: delega el trabajo a funciones.
Conclusión
match-case permite expresar patrones estructurales de forma clara. Puede comparar literales, descomponer secuencias, leer claves de diccionarios, reconocer objetos y aplicar guards. Su mayor ventaja aparece al interpretar datos con varias formas conocidas. Cuando solo necesitas una condición booleana sencilla, if-elif sigue siendo más directo. Utiliza la herramienta que comunique mejor la estructura y mantén los casos pequeños, ordenados y cubiertos por pruebas.






