Python 3.10 introdujo el structural pattern matching con la PEP 634. La instrucción match/case no es un simple switch: compara la estructura del objeto no solo su valor y puede extraer variables del interior en el mismo paso. Es especialmente útil para parsear respuestas de API, implementar CLIs y escribir máquinas de estado.
Sintaxis básica y patrones de valor
def clasificar_http(codigo):
match codigo:
case 200:
return "OK"
case 404:
return "No encontrado"
case 500 | 502 | 503: # patrón OR
return "Error de servidor"
case _: # caso por defecto (wildcard)
return f"Código desconocido: {codigo}"
print(clasificar_http(404)) # No encontrado
print(clasificar_http(502)) # Error de servidor
Patrones de secuencia
def procesar_comando(tokens):
match tokens:
case ["salir"]:
return "Cerrando..."
case ["ayuda"]:
return "Comandos: salir, ayuda, listar"
case ["listar", directorio]:
return f"Listando: {directorio}"
case ["mover", origen, destino]:
return f"Moviendo {origen} ? {destino}"
case ["buscar", *terminos]: # resto en lista
return f"Buscando: {' '.join(terminos)}"
case _:
return "Comando no reconocido"
print(procesar_comando(["listar", "/home"]))
# Listando: /home
print(procesar_comando(["mover", "a.txt", "b.txt"]))
# Moviendo a.txt ? b.txt
Patrones de mapping (diccionarios)
def manejar_evento(evento):
match evento:
case {"tipo": "click", "boton": boton, "x": x, "y": y}:
return f"Click con botón {boton} en ({x}, {y})"
case {"tipo": "teclado", "tecla": tecla}:
return f"Tecla pulsada: {tecla}"
case {"tipo": tipo}:
return f"Evento desconocido: {tipo}"
case _:
return "Evento sin tipo"
print(manejar_evento({"tipo": "click", "boton": 1, "x": 100, "y": 200}))
# Click con botón 1 en (100, 200)
Patrones de clase
from dataclasses import dataclass
@dataclass
class Punto:
x: float
y: float
@dataclass
class Circulo:
centro: Punto
radio: float
def describir_forma(forma):
match forma:
case Punto(x=0, y=0):
return "Punto en el origen"
case Punto(x=x, y=0):
return f"Punto en el eje X: ({x}, 0)"
case Punto(x=x, y=y):
return f"Punto genérico: ({x}, {y})"
case Circulo(radio=r) if r > 10:
return f"Círculo grande (r={r})"
case Circulo(radio=r):
return f"Círculo pequeño (r={r})"
print(describir_forma(Punto(3, 0))) # Punto en el eje X: (3, 0)
print(describir_forma(Circulo(Punto(0,0), 15))) # Círculo grande (r=15)
Guards: añadir condiciones con if
def clasificar_temperatura(temp):
match temp:
case t if t < 0:
return f"{t}°C bajo cero"
case t if t < 20:
return f"{t}°C frío"
case t if t < 30:
return f"{t}°C agradable"
case t:
return f"{t}°C calor"
print(clasificar_temperatura(-5)) # -5°C bajo cero
print(clasificar_temperatura(25)) # 25°C agradable
Máquina de estados con match/case
estado = "inicio"
def transicion(estado, evento):
match (estado, evento):
case ("inicio", "conectar"):
return "conectado"
case ("conectado", "autenticar"):
return "autenticado"
case ("autenticado", "desconectar") | ("conectado", "desconectar"):
return "inicio"
case _:
return estado # sin transición
estado = transicion(estado, "conectar") # conectado
estado = transicion(estado, "autenticar") # autenticado
estado = transicion(estado, "desconectar") # inicio
print(estado) # inicio
match/case destaca cuando la estructura del dato es el criterio de decisión, no solo su valor. Si solo comparas un entero o cadena con un conjunto fijo de literales, un diccionario de dispatch sigue siendo más simple.
