13. Tomar decisiones con match y case (Structural Pattern Matching)
Cuando tu programa necesita tomar decisiones basadas en múltiples valores o patrones posibles, ya has aprendido a usar cadenas if-elif-else del Capítulo 8. Python 3.10 introdujo una alternativa potente llamada structural pattern matching usando sentencias match y case. Esta característica proporciona una forma más limpia y expresiva de manejar escenarios complejos de toma de decisiones.
El pattern matching va más allá de simples comparaciones de valores. Te permite hacer coincidencias contra la estructura y la forma de los datos, extraer valores de objetos complejos y expresar decisiones con múltiples caminos en un formato más legible. Aunque las cadenas if-elif-else funcionan perfectamente bien para muchas situaciones, las sentencias match-case brillan cuando estás lidiando con múltiples casos distintos, especialmente al trabajar con datos estructurados.
13.1) Introducción a las sentencias match y case (Basándose en if-elif del Capítulo 8)
13.1.1) La estructura básica de match-case
Una sentencia match examina un valor (llamado el sujeto) y lo compara con uno o más patrones(patterns) definidos en cláusulas case. Cuando un patrón coincide, Python ejecuta el bloque de código asociado con ese caso.
Esta es la estructura básica:
match subject:
case pattern1:
# Código a ejecutar si pattern1 coincide
case pattern2:
# Código a ejecutar si pattern2 coincide
case pattern3:
# Código a ejecutar si pattern3 coincideEmpecemos con un ejemplo simple que demuestra el concepto fundamental:
# Manejador simple de códigos de estado HTTP
status_code = 404
match status_code:
case 200:
print("Success: Request completed")
case 404:
print("Error: Page not found")
case 500:
print("Error: Server error")Output:
Error: Page not foundEn este ejemplo, la sentencia match examina status_code (el sujeto). Python comprueba cada patrón de case en orden. Cuando encuentra que status_code es igual a 404, ejecuta el bloque de código correspondiente y luego sale de la sentencia match. Los casos restantes no se comprueban.
13.1.2) En qué se diferencia match-case de if-elif-else
Puede que te preguntes: "¿No podría escribir esto con if-elif-else?" Sí, podrías:
status_code = 404
if status_code == 200:
print("Success: Request completed")
elif status_code == 404:
print("Error: Page not found")
elif status_code == 500:
print("Error: Server error")Output:
Error: Page not foundAmbas versiones producen el mismo resultado. Sin embargo, match-case ofrece varias ventajas:
- Intención más clara: La sentencia
matchmuestra explícitamente que estás comprobando un valor contra múltiples posibilidades - Menos repetición: No repites el nombre de la variable en cada comparación
- Patrones(patterns) más potentes: Como veremos,
match-casepuede hacer mucho más que simples comprobaciones de igualdad - Mejor legibilidad: Para árboles de decisión complejos,
match-casesuele ser más fácil de entender
13.1.3) Cuando ningún patrón coincide
¿Qué pasa si ninguno de los patrones coincide? La sentencia match simplemente termina sin ejecutar ningún bloque de caso:
# Verificador de rol de usuario
user_role = "guest"
match user_role:
case "admin":
print("Full system access granted")
case "moderator":
print("Content management access granted")
case "editor":
print("Editing access granted")
print("Role check complete")Output:
Role check completeComo "guest" no coincide con ninguno de los patrones, no se ejecuta ningún bloque case. El programa continúa con el código después de la sentencia match. Este comportamiento es importante de entender: a diferencia de las cadenas if-elif-else donde puedes añadir una cláusula final else para capturar todos los demás casos, una sentencia match básica sin un patrón que lo abarque todo no hará nada silenciosamente si ningún patrón coincide.
13.1.4) Ejemplo práctico: Sistema de selección de menú
Construyamos un ejemplo más completo que demuestre la claridad de match-case para manejar elecciones del usuario:
# Sistema de pedidos de restaurante
menu_choice = 3
match menu_choice:
case 1:
item = "Caesar Salad"
price = 8.99
print(f"You ordered: {item} - ${price}")
case 2:
item = "Grilled Chicken"
price = 14.99
print(f"You ordered: {item} - ${price}")
case 3:
item = "Vegetable Pasta"
price = 12.99
print(f"You ordered: {item} - ${price}")
case 4:
item = "Chocolate Cake"
price = 6.99
print(f"You ordered: {item} - ${price}")
print("Order submitted to kitchen")Output:
You ordered: Vegetable Pasta - $12.99
Order submitted to kitchenEste ejemplo muestra cómo cada caso puede contener múltiples sentencias. Cuando menu_choice coincide con 3, Python ejecuta las tres líneas en ese bloque de caso: asignar item, asignar price e imprimir la confirmación del pedido.
13.2) Usar el comodín _, patrones literales y múltiples patrones
13.2.1) El patrón comodín: capturar todo lo demás
El guion bajo _ es un patrón especial que coincide con cualquier cosa. Normalmente se usa como el último caso para manejar todos los valores que no coincidieron con los patrones anteriores, similar a una cláusula else final en una cadena if-elif-else:
# Manejador de códigos de estado HTTP con caso por defecto
status_code = 403
match status_code:
case 200:
print("Success: Request completed")
case 404:
print("Error: Page not found")
case 500:
print("Error: Server error")
case _:
print(f"Unhandled status code: {status_code}")Output:
Unhandled status code: 403El patrón _ actúa como un comodín. Como 403 no coincide con ninguno de los casos específicos, el patrón comodín coincide y ejecuta su bloque. El patrón comodín coincidirá con cualquier valor, así que siempre debería colocarse al final: cualquier caso posterior nunca se ejecutaría.
Aquí tienes por qué el comodín es útil en la práctica:
# Planificador de días de la semana
day = "Saturday"
match day:
case "Monday":
print("Team meeting at 9 AM")
case "Wednesday":
print("Project review at 2 PM")
case "Friday":
print("Weekly report due")
case _:
print(f"{day}: No scheduled events")Output:
Saturday: No scheduled eventsSin el patrón comodín, si day fuera "Saturday", "Sunday" o cualquier otro valor, la sentencia match terminaría silenciosamente sin salida. El comodín asegura que manejas casos inesperados o no especificados de forma elegante.
13.2.2) Patrones literales: coincidir con valores específicos
Los patrones literales(literal patterns) coinciden con valores exactos. Ya los hemos estado usando: los números, cadenas y valores booleanos son todos patrones literales:
# Controlador de semáforo
light_color = "yellow"
match light_color:
case "green":
print("Go")
case "yellow":
print("Caution: Light changing soon")
case "red":
print("Stop")
case _:
print("Invalid light color")Output:
Caution: Light changing soonPuedes usar patrones literales de distintos tipos, y match compara tanto el valor como su tipo:
# Validador de configuración (usando distintos tipos literales)
setting_value = True
match setting_value:
case True: # literal booleano
print("Feature enabled")
case False: # literal booleano
print("Feature disabled")
case None: # literal None
print("Feature not configured")
case 0: # literal entero
print("Feature explicitly turned off")
case "auto": # literal de cadena
print("Feature set to automatic mode")
case _:
print("Invalid configuration value")Output:
Feature enabledLos patrones literales funcionan con enteros, floats, cadenas, booleanos y None. Python comprueba la igualdad usando las mismas reglas que el operador ==.
13.2.3) Múltiples patrones con el operador OR
A veces quieres ejecutar el mismo código para varios valores distintos. Puedes combinar múltiples patrones usando el operador | (pipe), que significa "o":
# Detector de fin de semana
day = "Saturday"
match day:
case "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday":
print("It's a weekday - time to work!")
case "Saturday" | "Sunday":
print("It's the weekend - time to relax!")
case _:
print("Invalid day name")Output:
It's the weekend - time to relax!El operador | te permite especificar múltiples patrones que deberían desencadenar la misma acción. Si el sujeto coincide con cualquiera de los patrones separados por |, se ejecuta ese caso. Esto es mucho más limpio que escribir casos separados con bloques de código idénticos.
Puedes mezclar distintos tipos de patrones con |:
# Validador de entrada para preguntas de sí/no
response = "yes"
match response:
case True | "yes":
print("You confirmed the action")
case False | "no":
print("You cancelled the action")
case _:
print("Please answer yes or no")Output:
You confirmed the action13.2.4) Capturar qué alternativa coincidió con as
Al usar múltiples patrones con |, puede que quieras saber qué valor específico coincidió. Puedes usar la palabra clave as para capturar el valor coincidente:
# Manejador de códigos de estado con respuestas agrupadas
status = 201
match status:
case 200 | 201 | 202 | 204 as success_code:
print(f"Success: {success_code}")
case 400 | 401 | 403 | 404 as client_error:
print(f"Client error: {client_error}")
case 500 | 502 | 503 as server_error:
print(f"Server error: {server_error}")
case _:
print("Unknown status code")Output:
Success: 201La palabra clave as crea una variable de enlace que captura la alternativa que haya coincidido. En este ejemplo, success_code queda enlazada a 201 porque ese es el valor específico que coincidió de las alternativas 200 | 201 | 202 | 204.
Aquí tienes otro ejemplo que muestra cómo esto es útil para el logging:
# Procesador de nivel de log
log_level = "WARN"
match log_level:
case "DEBUG" | "TRACE" as level:
print(f"Verbose logging: {level}")
print("Detailed diagnostic information will be recorded")
case "INFO" | "NOTICE" as level:
print(f"Informational: {level}")
print("Normal operation messages will be recorded")
case "WARN" | "WARNING" as level:
print(f"Warning level: {level}")
print("Potential issues detected")
case "ERROR" | "FATAL" | "CRITICAL" as level:
print(f"Error level: {level}")
print("Immediate attention required")
case _:
print("Unknown log level")Output:
Warning level: WARN
Potential issues detected13.3) Extraer valores con variables de enlace
13.3.1) ¿Qué son las variables de enlace?
Hasta ahora, hemos hecho coincidencias contra valores literales. Pero el pattern matching se vuelve realmente potente cuando puedes capturar o extraer partes de los datos con los que haces la coincidencia. Una variable de enlace(binding variable) (también llamada un patrón de captura(capture pattern)) es un nombre en un patrón que captura el valor coincidente y lo pone a disposición dentro del bloque del caso.
Aquí tienes un ejemplo simple:
# Captura simple de valor
command = "save"
match command:
case "quit":
print("Exiting program")
case action: # Esta es una variable de enlace
print(f"Executing action: {action}")Output:
Executing action: saveEl patrón action es una variable de enlace. Coincide con cualquier valor (como el comodín _), pero a diferencia de _, captura ese valor y lo asigna al nombre action. Dentro del bloque del caso, puedes usar action para referirte al valor coincidente.
Distinción importante: Una variable de enlace coincide con cualquier cosa, igual que _. La diferencia es que _ descarta el valor, mientras que una variable de enlace lo captura para usarlo dentro del bloque del caso.
13.3.2) Variables de enlace vs comodines
Comparemos las variables de enlace y los comodines directamente:
# Usando comodín: el valor no se captura
status = 403
match status:
case 200:
print("Success")
case _:
print("Some other status code") # No puedes acceder al valor realOutput:
Some other status codeAhora con una variable de enlace:
# Usando variable de enlace: el valor se captura
status = 403
match status:
case 200:
print("Success")
case code: # La variable de enlace captura el valor
print(f"Status code {code} received")Output:
Status code 403 receivedLa variable de enlace code captura el valor 403, haciéndolo disponible dentro del bloque del caso. Esto es útil cuando necesitas trabajar con el valor real que no coincidió con tus patrones específicos.
13.3.3) Coincidir patrones de tuplas y extraer componentes
El pattern matching se vuelve especialmente potente con datos estructurados como las tuplas. Puedes coincidir con la forma de una tupla y extraer sus componentes simultáneamente. Aunque estudiaremos las tuplas en detalle en el Capítulo 15, este ejemplo se centra solo en cómo funcionan los patrones de tuplas en sentencias match.
# Sistema de coordenadas: coincidencia con un patrón de tupla
point = (3, 7)
match point:
case (0, 0):
print("Origin point")
case (0, y): # Coincide con cualquier punto sobre el eje y
print(f"On y-axis at y={y}")
case (x, 0): # Coincide con cualquier punto sobre el eje x
print(f"On x-axis at x={x}")
case (x, y): # Coincide con cualquier otro punto
print(f"Point at coordinates ({x}, {y})")Output:
Point at coordinates (3, 7)Desglosemos lo que está pasando:
- El sujeto
pointes la tupla(3, 7) - Python comprueba cada patrón de caso en orden
- Los tres primeros patrones no coinciden porque requieren el valor
0en una posición específica, y la tupla(3, 7)no tiene ningún elemento igual a0 - El patrón
(x, y)coincide porque es una tupla de dos elementos - Python enlaza
xa3eya7 - El bloque del caso se ejecuta con estos valores capturados
Aquí tienes otro ejemplo que muestra distintos patrones de tuplas:
# Analizador de color RGB
color = (255, 0, 0)
match color:
case (0, 0, 0):
print("Black")
case (255, 255, 255):
print("White")
case (r, 0, 0): # Rojo puro con intensidad variable
print(f"Pure red with intensity {r}")
case (0, g, 0): # Verde puro
print(f"Pure green with intensity {g}")
case (0, 0, b): # Azul puro
print(f"Pure blue with intensity {b}")
case (r, g, b): # Cualquier otro color
print(f"RGB color: red={r}, green={g}, blue={b}")Output:
Pure red with intensity 255Esta entrada coincide con el patrón (r, 0, 0) porque la tupla tiene tres elementos, los dos últimos son 0, y el primer valor queda enlazado a r.
13.3.4) Coincidir patrones de listas
También puedes coincidir con patrones de listas y extraer elementos. Cubriremos las listas en detalle en el Capítulo 14; por ahora, este ejemplo se centra en cómo funcionan los patrones de listas en sentencias match:
# Comando con argumentos
command = ["move", "north", "5"]
match command:
case ["quit"]:
print("Exiting game")
case ["look"]:
print("You look around the room")
case ["move", direction]:
print(f"Moving {direction}")
case ["move", direction, distance]:
print(f"Moving {direction} for {distance} steps")
case _:
print("Unknown command")Output:
Moving north for 5 stepsEl patrón ["move", direction, distance] coincide con una lista de tres elementos donde el primer elemento es "move". Captura el segundo elemento como direction y el tercero como distance.
Aquí tienes un ejemplo práctico con longitudes de lista variables:
# Procesador de artículos del carrito de compra
item = ["laptop", 999.99, 2]
match item:
case [name]: # Artículo con solo un nombre
print(f"Item: {name} (no price or quantity specified)")
case [name, price]: # Artículo con nombre y precio
print(f"Item: {name}, Price: ${price}, Quantity: 1 (default)")
case [name, price, quantity]: # Información completa del artículo
total = price * quantity
print(f"Item: {name}, Price: ${price}, Quantity: {quantity}")
print(f"Subtotal: ${total}")
case _:
print("Invalid item format")Output:
Item: laptop, Price: $999.99, Quantity: 2
Subtotal: $1999.98El caso [name, price, quantity] se ejecuta porque la lista tiene exactamente tres elementos, y cada elemento queda enlazado a su variable correspondiente.
13.3.5) Coincidir patrones de diccionarios
El pattern matching funciona también con diccionarios, lo que te permite hacer coincidencias con claves específicas y extraer sus valores. Aunque estudiaremos los diccionarios en detalle en el Capítulo 16, esta sección se centra solo en cómo funcionan los patrones de diccionarios en sentencias match.
# Procesador de perfil de usuario
user = {"name": "Alice", "role": "admin", "active": True}
match user:
case {"role": "admin", "active": True}:
print("Active administrator - full access granted")
case {"role": "admin", "active": False}:
print("Inactive administrator - access suspended")
case {"role": role, "active": True}: # Captura el valor de role
print(f"Active user with role: {role}")
case {"role": role, "active": False}:
print(f"Inactive user with role: {role}")
case _:
print("Invalid user profile")Output:
Active administrator - full access grantedEl caso {"role": "admin", "active": True} se ejecuta porque los patrones de diccionario requieren coincidir con pares clave–valor, y esta coincidencia exacta se comprueba antes que patrones más generales.
Los patrones de diccionario son flexibles: coinciden si las claves especificadas existen con los valores especificados, incluso si el diccionario tiene claves adicionales:
# Manejador de respuesta de API
response = {"status": "success", "data": {"id": 123, "name": "Product"}, "timestamp": "2025-12-17"}
match response:
case {"status": "error", "message": msg}:
print(f"Error occurred: {msg}")
case {"status": "success", "data": data}:
print(f"Success! Data received: {data}")
case _:
print("Unknown response format")Output:
Success! Data received: {'id': 123, 'name': 'Product'}El patrón {"status": "success", "data": data} coincide aunque el diccionario tenga una clave adicional "timestamp". El patrón solo requiere que las claves especificadas existan con los valores especificados (o patrones).
13.3.6) Combinar literales y variables de enlace
Puedes mezclar patrones literales y variables de enlace para crear una lógica de coincidencia sofisticada: A diferencia de los ejemplos de tuplas anteriores que se centraban en coincidir estructura y posición, este ejemplo muestra cómo se pueden combinar valores literales y variables de enlace para implementar lógica de decisión del mundo real.
# Enrutador de solicitudes HTTP
request = ("GET", "/api/users", 42)
match request:
case ("GET", "/", None):
print("Homepage request")
case ("GET", path, None):
print(f"GET request for: {path}")
case ("POST", path, data):
print(f"POST request to {path} with data: {data}")
case ("GET", path, user_id):
print(f"GET request for {path} with user ID: {user_id}")
case _:
print("Unsupported request type")Output:
GET request for /api/users with user ID: 42Este ejemplo muestra cómo puedes coincidir con valores específicos (como "GET") mientras capturas otros (como path y user_id) en el mismo patrón.
13.3.7) Ejemplo práctico: Manejador de eventos
Construyamos un ejemplo completo que demuestre el poder de las variables de enlace:
# Manejador de eventos del juego
event = ("player_move", {"x": 10, "y": 5, "speed": 2})
match event:
case ("player_move", {"x": x, "y": y}):
print(f"Player moved to position ({x}, {y})")
case ("player_attack", {"target": target, "damage": damage}):
print(f"Player attacked {target} for {damage} damage")
case ("item_pickup", {"item": item_name}):
print(f"Player picked up: {item_name}")
case ("game_over", {"score": final_score}):
print(f"Game ended. Final score: {final_score}")
case (event_type, data):
print(f"Unknown event type: {event_type}")
print(f"Event data: {data}")Output:
Player moved to position (10, 5)Este manejador de eventos coincide con tuplas que contienen un tipo de evento y un diccionario de datos del evento. Extrae valores específicos del diccionario según el tipo de evento, lo que hace que sea fácil procesar distintos tipos de eventos con código limpio y legible.
13.4) Añadir condiciones extra con la guarda if
13.4.1) ¿Qué son las guardas?
A veces necesitas que un patrón coincida y comprobar una condición adicional. Una guarda if(if guard) es una condición extra que puedes añadir a un patrón de caso usando la palabra clave if. El caso solo coincide si tanto el patrón coincide como la condición de la guarda es verdadera.
Esta es la sintaxis:
match subject:
case pattern if condition:
# El código se ejecuta solo si el patrón coincide Y condition es TrueVeamos un ejemplo simple:
# Control de acceso por edad
age = 16
match age:
case age if age >= 18:
print("Adult - full access granted")
case age if age >= 13:
print("Teen - limited access granted")
case age if age >= 0:
print("Child - parental supervision required")
case _:
print("Invalid age")Output:
Teen - limited access grantedEn este ejemplo, la variable de enlace age captura el valor, y la guarda if age >= 13 añade una condición adicional. El caso coincide solo si el valor es 13 o mayor. Como age es 16, el segundo caso coincide y se ejecuta.
13.4.2) Cómo se evalúan las guardas
Entender el orden de evaluación es importante. Aquí tienes una visualización detallada que muestra cómo interactúan las guardas con el pattern matching:
Python primero comprueba si el patrón coincide. Solo si el patrón coincide, Python evalúa la condición de la guarda. Si la guarda es falsa, Python pasa al siguiente caso, aunque el patrón hubiera coincidido.
Aquí tienes un ejemplo que lo demuestra:
# Sistema de advertencia de temperatura
temperature = 25
match temperature:
case temp if temp > 35:
print(f"Extreme heat warning: {temp}°C")
case temp if temp > 30:
print(f"High temperature alert: {temp}°C")
case temp if temp > 20:
print(f"Comfortable temperature: {temp}°C")
case temp if temp > 10:
print(f"Cool temperature: {temp}°C")
case temp:
print(f"Cold temperature: {temp}°C")Output:
Comfortable temperature: 25°CCada caso usa la variable de enlace temp para capturar el valor de temperatura, y luego aplica una guarda para comprobar si cae dentro de un rango específico. Los casos se comprueban en orden, así que se ejecuta el primer caso que coincide y tiene una guarda verdadera.
13.4.3) Guardas con patrones literales
Puedes combinar guardas con patrones literales para crear coincidencias más específicas:
# Calculadora de descuento basada en el tipo de artículo y la cantidad
item = ("book", 5)
match item:
case ("book", quantity) if quantity >= 10:
discount = 0.20 # 20% de descuento para 10+ libros
print(f"Bulk book order: {quantity} books, {discount*100}% discount")
case ("book", quantity) if quantity >= 5:
discount = 0.10 # 10% de descuento para 5-9 libros
print(f"Book order: {quantity} books, {discount*100}% discount")
case ("book", quantity):
discount = 0.0 # Sin descuento para menos de 5 libros
print(f"Book order: {quantity} books, no discount")
case (item_type, quantity):
print(f"Order: {quantity} {item_type}(s)")Output:
Book order: 5 books, 10.0% discountEl patrón ("book", quantity) coincide con una tupla donde el primer elemento es "book". La guarda if quantity >= 5 añade la condición de que la cantidad debe ser al menos 5.
13.4.4) Guardas con condiciones complejas
Las guardas pueden usar cualquier expresión booleana, incluyendo condiciones complejas con and, or y not:
# Evaluador de calificaciones de estudiante con consideración de asistencia
student = {"name": "Bob", "grade": 85, "attendance": 75}
match student:
case {"grade": g, "attendance": a} if g >= 90 and a >= 90:
status = "Excellent"
print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
case {"grade": g, "attendance": a} if g >= 80 and a >= 80:
status = "Good"
print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
case {"grade": g, "attendance": a} if g >= 70 and a >= 70:
status = "Satisfactory"
print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
case {"grade": g, "attendance": a} if g >= 60 or a >= 60:
status = "Needs Improvement"
print(f"Grade: {g}, Attendance: {a}% - Status: {status}")
case _:
print("Failing - immediate intervention required")Output:
Grade: 85, Attendance: 75% - Status: SatisfactoryLa guarda if g >= 70 and a >= 70 requiere que tanto la nota como la asistencia sean al menos 70. Como Bob tiene una nota de 85 y una asistencia del 75%, este caso coincide.
13.4.5) Ejemplo práctico: Sistema de autenticación de usuarios
Construyamos un ejemplo completo que use guardas para implementar un sistema de autenticación realista:
# Autenticación de usuario con acceso basado en roles
user = {"username": "alice", "role": "admin", "active": True, "login_attempts": 0}
match user:
case {"active": False}:
print("Account suspended - contact administrator")
case {"login_attempts": attempts} if attempts >= 3:
print("Account locked due to too many failed login attempts")
case {"role": "admin", "active": True}:
print("Admin access granted - full system privileges")
case {"role": "moderator", "active": True}:
print("Moderator access granted - content management privileges")
case {"role": role, "active": True} if role in ["editor", "author"]:
print(f"{role.capitalize()} access granted - content creation privileges")
case {"role": "user", "active": True}:
print("User access granted - basic privileges")
case _:
print("Access denied - invalid user profile")Output:
Admin access granted - full system privilegesEste ejemplo demuestra cómo las guardas pueden implementar lógica de negocio compleja. El sistema comprueba múltiples condiciones: estado de la cuenta, intentos de inicio de sesión y permisos basados en roles. Cada caso maneja un escenario específico, haciendo que la lógica de autenticación sea clara y mantenible.
13.5) Coincidir con formas simples de secuencias y mapeos
13.5.1) Coincidir secuencias de longitud variable
A veces necesitas coincidir con secuencias de longitudes variables. El pattern matching de Python lo soporta con el operador *, que captura cero o más elementos.
# Analizador de comandos con argumentos variables
command = ["copy", "file1.txt", "file2.txt", "file3.txt", "backup/"]
match command:
case ["help"]:
print("Available commands: copy, move, delete")
case ["copy", source, destination]:
print(f"Copying {source} to {destination}")
case ["copy", *sources, destination]:
print(f"Copying {len(sources)} files to {destination}")
print(f"Source files: {sources}")
case ["delete", *files]:
print(f"Deleting {len(files)} file(s): {files}")
case _:
print("Unknown command")Output:
Copying 3 files to backup/
Source files: ['file1.txt', 'file2.txt', 'file3.txt']El patrón ["copy", *sources, destination] coincide con una lista que empieza con "copy", termina con un destino y tiene cualquier número de archivos fuente en medio. *sources captura todos los elementos intermedios como una lista.
Importante: Solo puedes usar un patrón * por patrón de secuencia, y captura elementos como una lista:
# Analizador de entradas de log
log_entry = ["2025-12-17", "10:30:45", "ERROR", "Database", "connection", "timeout"]
match log_entry:
case [date, time, "ERROR", *error_details]:
print(f"Error on {date} at {time}")
print(f"Error details: {' '.join(error_details)}")
case [date, time, "WARNING", *warning_details]:
print(f"Warning on {date} at {time}")
print(f"Warning details: {' '.join(warning_details)}")
case [date, time, level, *message]:
print(f"{level} on {date} at {time}: {' '.join(message)}")Output:
Error on 2025-12-17 at 10:30:45
Error details: Database connection timeout13.5.2) Combinar patrones de secuencia con guardas
Puedes usar guardas con patrones de secuencia para añadir condiciones adicionales:
# Analizador de lista de notas
grades = [85, 92, 78, 95, 88]
match grades:
case []:
print("No grades recorded")
case [grade] if grade >= 90:
print(f"Single excellent grade: {grade}")
case [grade] if grade < 60:
print(f"Single failing grade: {grade}")
case [*all_grades] if len(all_grades) >= 5 and sum(all_grades) / len(all_grades) >= 90:
average = sum(all_grades) / len(all_grades)
print(f"Excellent performance! Average: {average:.1f}")
case [*all_grades] if len(all_grades) >= 5:
average = sum(all_grades) / len(all_grades)
print(f"Performance review: {len(all_grades)} grades, Average: {average:.1f}")
case [*all_grades]:
print(f"Insufficient data: only {len(all_grades)} grade(s)")Output:
Performance review: 5 grades, Average: 87.6El patrón [*all_grades] captura todos los elementos de la lista, y la guarda comprueba tanto la longitud como calcula el promedio para determinar el mensaje apropiado.
El pattern matching con match y case proporciona una forma potente y expresiva de manejar la toma de decisiones complejas en Python. Desde la coincidencia simple de valores hasta patrones estructurales sofisticados con guardas, esta característica te permite escribir código más limpio y mantenible para manejar múltiples casos y extraer datos de estructuras complejas.
A medida que sigas aprendiendo Python, verás que el pattern matching complementa la lógica condicional que aprendiste en el Capítulo 8, ofreciendo una alternativa elegante cuando lidias con múltiples casos distintos, especialmente al trabajar con datos estructurados. La clave es elegir la herramienta adecuada para cada situación: usa if-elif-else para condiciones simples y lógica booleana, y recurre a match-case cuando estés comprobando un valor contra múltiples posibilidades o trabajando con datos estructurados que necesitan extracción basada en patrones.