Python
Tutorial Python: Pattern matching
Aprende pattern matching en Python 3.10 con match-case para manejar datos complejos y estructuras con guards y captura de variables.
Aprende Python y certifícateSintaxis match-case
El pattern matching es una característica moderna de Python introducida en la versión 3.10 que permite examinar una variable y ejecutar diferentes bloques de código según su estructura y valor. La sintaxis match-case
proporciona una alternativa más potente y expresiva a las tradicionales estructuras if-elif-else
para ciertos escenarios.
La estructura básica de una expresión match-case
sigue este patrón:
match expresion:
case patron1:
# Código si expresion coincide con patron1
case patron2:
# Código si expresion coincide con patron2
case _:
# Código por defecto (similar a else)
Casos simples
La forma más básica de usar match-case
es para comparar valores literales:
def analizar_estado(codigo):
match codigo:
case 200:
return "OK"
case 404:
return "No encontrado"
case 500:
return "Error del servidor"
case _:
return f"Código desconocido: {codigo}"
print(analizar_estado(200)) # OK
print(analizar_estado(418)) # Código desconocido: 418
Patrones OR
Puedes combinar varios patrones usando el operador |
(OR):
def clasificar_numero(n):
match n:
case 0:
return "Cero"
case 1 | 3 | 5 | 7 | 9:
return "Impar de un dígito"
case 2 | 4 | 6 | 8:
return "Par de un dígito"
case _:
return "Número de múltiples dígitos"
print(clasificar_numero(5)) # Impar de un dígito
print(clasificar_numero(8)) # Par de un dígito
print(clasificar_numero(42)) # Número de múltiples dígitos
Captura de valores
Puedes capturar valores en variables dentro de los patrones:
def procesar_comando(comando):
match comando.split():
case ["salir"]:
return "Saliendo del programa"
case ["guardar", nombre_archivo]:
return f"Guardando en {nombre_archivo}"
case ["abrir", nombre_archivo]:
return f"Abriendo {nombre_archivo}"
case ["ayuda"]:
return "Comandos disponibles: salir, guardar, abrir, ayuda"
case _:
return "Comando no reconocido"
print(procesar_comando("guardar documento.txt")) # Guardando en documento.txt
print(procesar_comando("ayuda")) # Comandos disponibles: salir, guardar, abrir, ayuda
En este ejemplo, nombre_archivo
captura cualquier valor en la segunda posición de la lista.
Patrones con constantes
Para usar constantes en patrones, debes prefijarlas con _
:
MAX_USUARIOS = 100
MIN_USUARIOS = 10
def validar_usuarios(cantidad):
match cantidad:
case _MAX_USUARIOS:
return "Límite máximo alcanzado"
case _MIN_USUARIOS:
return "Límite mínimo alcanzado"
case _:
return "Cantidad dentro de los límites"
print(validar_usuarios(100)) # Límite máximo alcanzado
print(validar_usuarios(50)) # Cantidad dentro de los límites
Patrones con clases
El match-case
es particularmente útil con objetos y clases:
class Punto:
def __init__(self, x, y):
self.x = x
self.y = y
def clasificar_punto(punto):
match punto:
case Punto(x=0, y=0):
return "Origen"
case Punto(x=0, y=y):
return f"En el eje Y, coordenada {y}"
case Punto(x=x, y=0):
return f"En el eje X, coordenada {x}"
case Punto(x=x, y=y) if x == y:
return f"En la diagonal, coordenada {x}"
case Punto():
return "Punto en otra posición"
case _:
return "No es un punto"
print(clasificar_punto(Punto(0, 0))) # Origen
print(clasificar_punto(Punto(5, 0))) # En el eje X, coordenada 5
print(clasificar_punto(Punto(3, 3))) # En la diagonal, coordenada 3
Patrones con secuencias
El match-case
es muy potente para trabajar con secuencias como listas y tuplas:
def analizar_coordenadas(coords):
match coords:
case []:
return "Lista vacía"
case [x, y]:
return f"Punto 2D: ({x}, {y})"
case [x, y, z]:
return f"Punto 3D: ({x}, {y}, {z})"
case [x, y, *resto]:
return f"Punto multidimensional: empieza con ({x}, {y}) y continúa con {resto}"
case _:
return "No es una lista de coordenadas"
print(analizar_coordenadas([])) # Lista vacía
print(analizar_coordenadas([10, 20])) # Punto 2D: (10, 20)
print(analizar_coordenadas([1, 2, 3, 4])) # Punto multidimensional: empieza con (1, 2) y continúa con [3, 4]
El operador *
captura cualquier número de elementos restantes en una lista.
Patrones anidados
Puedes crear patrones anidados para estructuras de datos complejas:
def procesar_datos(datos):
match datos:
case {"tipo": "usuario", "id": id_usuario, "detalles": {"nombre": nombre}}:
return f"Usuario {nombre} con ID {id_usuario}"
case {"tipo": "producto", "id": id_producto, "precio": precio}:
return f"Producto con ID {id_producto}, precio: {precio}"
case {"error": mensaje, "codigo": codigo}:
return f"Error {codigo}: {mensaje}"
case _:
return "Formato de datos desconocido"
print(procesar_datos({"tipo": "usuario", "id": 123, "detalles": {"nombre": "Ana"}}))
# Usuario Ana con ID 123
print(procesar_datos({"error": "Acceso denegado", "codigo": 403}))
# Error 403: Acceso denegado
Ventajas sobre if-elif-else
La sintaxis match-case
ofrece varias ventajas sobre las estructuras tradicionales:
- Legibilidad: El código es más claro y expresivo, especialmente para estructuras de datos complejas.
- Concisión: Reduce la cantidad de código necesario para manejar múltiples casos.
- Captura de valores: Permite extraer y nombrar partes de los datos de forma elegante.
- Patrones estructurales: Facilita la coincidencia basada en la estructura de los datos.
# Con if-elif-else
def analizar_punto_tradicional(punto):
if not isinstance(punto, tuple) or len(punto) != 2:
return "No es un punto 2D"
x, y = punto
if x == 0 and y == 0:
return "Origen"
elif x == 0:
return f"En el eje Y, coordenada {y}"
elif y == 0:
return f"En el eje X, coordenada {x}"
elif x == y:
return f"En la diagonal, coordenada {x}"
else:
return "Punto en otra posición"
# Con match-case
def analizar_punto_moderno(punto):
match punto:
case (0, 0):
return "Origen"
case (0, y):
return f"En el eje Y, coordenada {y}"
case (x, 0):
return f"En el eje X, coordenada {x}"
case (x, y) if x == y:
return f"En la diagonal, coordenada {x}"
case (_, _):
return "Punto en otra posición"
case _:
return "No es un punto 2D"
Consideraciones de rendimiento
El match-case
está optimizado para ser eficiente en la mayoría de los casos. Python compila los patrones en código optimizado, similar a una serie de comprobaciones condicionales. Sin embargo, para casos muy simples, un if-elif-else
tradicional puede ser ligeramente más rápido.
# Ejemplo de benchmark simple
import timeit
def test_if_else(valor):
if valor == 1:
return "uno"
elif valor == 2:
return "dos"
else:
return "otro"
def test_match_case(valor):
match valor:
case 1:
return "uno"
case 2:
return "dos"
case _:
return "otro"
# Para casos simples, la diferencia de rendimiento es mínima
tiempo_if = timeit.timeit(lambda: test_if_else(3), number=1000000)
tiempo_match = timeit.timeit(lambda: test_match_case(3), number=1000000)
La sintaxis match-case
brinda una forma más expresiva y potente de manejar múltiples condiciones en Python, especialmente cuando se trabaja con estructuras de datos complejas o cuando se necesita extraer componentes de los datos durante el proceso de coincidencia.
Patrones estructurales
Los patrones estructurales son una de las características más potentes del pattern matching en Python. Estos patrones permiten examinar y descomponer estructuras de datos complejas como diccionarios, listas, tuplas y objetos de clases personalizadas en una sola operación.
A diferencia de las comparaciones simples de valores, los patrones estructurales analizan la forma y composición interna de los datos. Esto nos permite escribir código más declarativo que se centra en "qué estamos buscando" en lugar de "cómo lo estamos buscando".
Patrones para diccionarios
Los diccionarios son estructuras de datos fundamentales en Python, y el pattern matching ofrece una sintaxis elegante para trabajar con ellos:
def procesar_configuracion(config):
match config:
case {"debug": True, "entorno": entorno}:
return f"Modo depuración activado en entorno {entorno}"
case {"usuarios": usuarios, "max_conexiones": max_con} if len(usuarios) > max_con:
return f"Advertencia: {len(usuarios)} usuarios exceden el límite de {max_con} conexiones"
case {"version": version, "api_key": _}:
return f"Configuración válida para API v{version}"
case {"error": mensaje, **resto}:
return f"Error en configuración: {mensaje}. Detalles adicionales: {resto}"
case {}:
return "Configuración vacía"
case _:
return "Formato de configuración no reconocido"
# Ejemplos de uso
print(procesar_configuracion({"debug": True, "entorno": "desarrollo"}))
# Modo depuración activado en entorno desarrollo
print(procesar_configuracion({"usuarios": ["ana", "juan", "elena"], "max_conexiones": 2}))
# Advertencia: 3 usuarios exceden el límite de 2 conexiones
Características importantes:
- Podemos verificar la presencia de claves específicas en el diccionario
- Podemos capturar valores asociados a claves específicas
- El operador
**resto
captura todas las claves restantes no especificadas - Podemos combinar patrones con guardas para condiciones adicionales
Patrones para secuencias (listas y tuplas)
El pattern matching brinda herramientas poderosas para trabajar con secuencias:
def analizar_serie_temporal(datos):
match datos:
case []:
return "Serie vacía"
case [único_valor]:
return f"Serie con un solo valor: {único_valor}"
case [primer, *_, último] if primer == último:
return f"Serie circular: comienza y termina con {primer}"
case [inicio, segundo, *resto]:
diferencia = segundo - inicio
return f"Serie que comienza con {inicio}, {segundo} (diferencia: {diferencia})"
case (año, mes, día):
return f"Fecha en formato tupla: {día}/{mes}/{año}"
case _:
return "Formato no reconocido"
# Ejemplos de uso
print(analizar_serie_temporal([10, 15, 20, 25, 10]))
# Serie circular: comienza y termina con 10
print(analizar_serie_temporal([5, 8, 11, 14, 17]))
# Serie que comienza con 5, 8 (diferencia: 3)
print(analizar_serie_temporal((2023, 11, 15)))
# Fecha en formato tupla: 15/11/2023
Características importantes:
- El operador
*_
captura cualquier número de elementos que no necesitamos nombrar - Podemos combinar captura de elementos al inicio y final de la secuencia
- El pattern matching distingue automáticamente entre listas y tuplas
- Podemos aplicar condiciones adicionales a los valores capturados
Patrones para clases y objetos personalizados
Una de las aplicaciones más potentes del pattern matching es con objetos de clases personalizadas:
class Evento:
def __init__(self, tipo, datos=None):
self.tipo = tipo
self.datos = datos or {}
class Respuesta:
def __init__(self, codigo, contenido=None):
self.codigo = codigo
self.contenido = contenido
def procesar_mensaje(mensaje):
match mensaje:
case Evento(tipo="click", datos={"x": x, "y": y}):
return f"Click en coordenadas ({x}, {y})"
case Evento(tipo="login", datos={"usuario": usuario}):
return f"Inicio de sesión de {usuario}"
case Evento(tipo="error", datos=datos) if "crítico" in datos:
return f"¡ERROR CRÍTICO! {datos['mensaje']}"
case Respuesta(codigo=200, contenido=contenido):
return f"Respuesta exitosa: {contenido}"
case Respuesta(codigo=codigo) if 400 <= codigo < 500:
return f"Error del cliente: código {codigo}"
case Respuesta(codigo=codigo) if 500 <= codigo < 600:
return f"Error del servidor: código {codigo}"
case _:
return "Mensaje no reconocido"
# Ejemplos de uso
print(procesar_mensaje(Evento("click", {"x": 100, "y": 200})))
# Click en coordenadas (100, 200)
print(procesar_mensaje(Respuesta(404, "Página no encontrada")))
# Error del cliente: código 404
print(procesar_mensaje(Evento("error", {"crítico": True, "mensaje": "Base de datos no disponible"})))
# ¡ERROR CRÍTICO! Base de datos no disponible
Características importantes:
- Podemos hacer coincidencia por atributos de objetos
- Podemos combinar patrones para atributos que son estructuras de datos complejas
- Funciona con cualquier clase que tenga los atributos especificados
- Podemos usar guardas para condiciones adicionales sobre los atributos
Patrones anidados y complejos
El verdadero poder de los patrones estructurales se revela cuando combinamos diferentes tipos de patrones:
def analizar_datos_iot(mensaje):
match mensaje:
case {"dispositivo": "sensor", "lecturas": [{"temp": t, "humedad": h} as lectura, *_]} if t > 30:
return f"Alerta: temperatura alta ({t}°C) en lectura: {lectura}"
case {"dispositivo": "gateway", "conexiones": conexiones} if len(conexiones) == 0:
return "Gateway sin dispositivos conectados"
case {"dispositivo": "actuador", "estado": {"encendido": True, "modo": modo}}:
return f"Actuador funcionando en modo {modo}"
case {"error": {"codigo": codigo, "detalles": detalles}}:
return f"Error {codigo}: {detalles}"
case _:
return "Formato de mensaje IoT no reconocido"
# Ejemplo de uso
datos = {
"dispositivo": "sensor",
"id": "temp-01",
"lecturas": [
{"temp": 32.5, "humedad": 60, "timestamp": "2023-11-15T14:30:00"},
{"temp": 31.8, "humedad": 62, "timestamp": "2023-11-15T14:35:00"}
]
}
print(analizar_datos_iot(datos))
# Alerta: temperatura alta (32.5°C) en lectura: {'temp': 32.5, 'humedad': 60, 'timestamp': '2023-11-15T14:30:00'}
En este ejemplo, utilizamos:
- Patrones anidados para examinar estructuras dentro de estructuras
- La palabra clave
as
para capturar un subpatrón completo - Guardas condicionales basadas en valores capturados
- Combinación de patrones de diccionario y lista
Patrones con clases de datos (dataclasses)
Las dataclasses de Python funcionan especialmente bien con pattern matching:
from dataclasses import dataclass
from typing import List, Optional
@dataclass
class Usuario:
nombre: str
rol: str
permisos: List[str]
@dataclass
class Recurso:
tipo: str
propietario: Optional[Usuario] = None
publico: bool = False
def verificar_acceso(usuario, recurso, accion):
match (usuario, recurso, accion):
case (Usuario(rol="admin", permisos=_), _, _):
return True
case (Usuario(permisos=permisos), Recurso(propietario=propietario), _) if usuario == propietario:
return True
case (Usuario(permisos=permisos), Recurso(publico=True), "leer"):
return True
case (Usuario(permisos=permisos), Recurso(tipo=tipo), accion) if f"{accion}_{tipo}" in permisos:
return True
case _:
return False
# Ejemplos de uso
admin = Usuario("Ana", "admin", ["eliminar_usuarios"])
usuario = Usuario("Carlos", "usuario", ["leer_documento", "editar_imagen"])
documento = Recurso("documento", usuario, False)
imagen_publica = Recurso("imagen", None, True)
print(verificar_acceso(admin, documento, "eliminar")) # True (admin)
print(verificar_acceso(usuario, documento, "leer")) # True (propietario)
print(verificar_acceso(usuario, imagen_publica, "leer")) # True (recurso público)
Las dataclasses proporcionan una estructura clara que se integra perfectamente con los patrones estructurales, haciendo que el código sea más legible y mantenible.
Consideraciones de diseño
Al trabajar con patrones estructurales, es importante tener en cuenta:
- Orden de los casos: Python evalúa los casos en orden secuencial, por lo que debes colocar los patrones más específicos antes que los más generales.
- Exhaustividad: Considera siempre incluir un caso por defecto (
case _:
) para manejar entradas inesperadas. - Complejidad: Si un patrón se vuelve demasiado complejo, considera dividirlo en múltiples funciones o usar guardas para claridad.
- Rendimiento: Para estructuras de datos muy grandes, el pattern matching puede ser menos eficiente que accesos directos, pero la claridad del código suele compensar esta diferencia.
Los patrones estructurales transforman la forma en que trabajamos con datos complejos en Python, permitiéndonos escribir código más declarativo, expresivo y fácil de entender.
Guards y captura de variables
El pattern matching en Python se vuelve aún más potente cuando combinamos la captura de variables con condiciones adicionales llamadas guards. Estos mecanismos nos permiten no solo identificar patrones estructurales, sino también establecer restricciones sobre los valores capturados y reutilizarlos de manera eficiente.
Captura de variables
La captura de variables es el proceso mediante el cual asignamos nombres a partes de la estructura que estamos analizando. Esto nos permite acceder a esos valores dentro del bloque de código correspondiente:
def analizar_punto(punto):
match punto:
case (x, y):
return f"Coordenadas capturadas: x={x}, y={y}"
print(analizar_punto((10, 20))) # Coordenadas capturadas: x=10, y=20
En este ejemplo, x
y y
son variables de captura que reciben los valores de la tupla. A diferencia de un desempaquetado normal, la captura en pattern matching ocurre solo si el patrón coincide completamente.
Captura con nombres específicos
Podemos capturar valores con nombres específicos en estructuras complejas:
def procesar_transaccion(transaccion):
match transaccion:
case {"monto": monto, "divisa": divisa, "fecha": fecha}:
return f"Transacción de {monto} {divisa} realizada el {fecha}"
print(procesar_transaccion({"monto": 100, "divisa": "EUR", "fecha": "2023-11-15", "id": "T123"}))
# Transacción de 100 EUR realizada el 2023-11-15
Aquí capturamos solo los campos que nos interesan, ignorando otros campos como id
.
Captura con el operador AS
El operador as
nos permite capturar subpatrones completos mientras seguimos analizando su estructura interna:
def analizar_medicion(datos):
match datos:
case {"sensor": id_sensor, "lecturas": [primera, *resto] as todas_lecturas}:
return f"Sensor {id_sensor}: {len(todas_lecturas)} lecturas, primera: {primera}"
datos = {"sensor": "temp-01", "lecturas": [32.5, 31.8, 30.2]}
print(analizar_medicion(datos))
# Sensor temp-01: 3 lecturas, primera: 32.5
En este ejemplo, capturamos tanto la primera lectura individualmente como la lista completa de lecturas usando as todas_lecturas
.
Guards (condiciones de guarda)
Los guards son condiciones adicionales que se evalúan después de que un patrón coincide. Se introducen con la palabra clave if
y permiten un control más preciso sobre cuándo debe ejecutarse un caso:
def clasificar_temperatura(temp):
match temp:
case float() | int() as t if t < 0:
return f"Temperatura bajo cero: {t}°C"
case float() | int() as t if 0 <= t <= 20:
return f"Temperatura fresca: {t}°C"
case float() | int() as t if 20 < t <= 30:
return f"Temperatura agradable: {t}°C"
case float() | int() as t:
return f"Temperatura calurosa: {t}°C"
case _:
return "Valor no es una temperatura válida"
print(clasificar_temperatura(-5)) # Temperatura bajo cero: -5°C
print(clasificar_temperatura(25)) # Temperatura agradable: 25°C
print(clasificar_temperatura(35)) # Temperatura calurosa: 35°C
print(clasificar_temperatura("35")) # Valor no es una temperatura válida
En este ejemplo:
- Primero verificamos si el valor coincide con el tipo (float o int)
- Luego capturamos ese valor en la variable
t
- Finalmente aplicamos una condición adicional con
if
Guards con múltiples variables
Los guards pueden utilizar múltiples variables capturadas en expresiones complejas:
def analizar_rango(rango):
match rango:
case (inicio, fin) if inicio < fin:
return f"Rango válido de {inicio} a {fin}"
case (inicio, fin) if inicio == fin:
return f"Rango degenerado: {inicio}"
case (inicio, fin):
return f"Rango invertido: {fin} a {inicio}"
print(analizar_rango((10, 20))) # Rango válido de 10 a 20
print(analizar_rango((5, 5))) # Rango degenerado: 5
print(analizar_rango((30, 20))) # Rango invertido: 20 a 30
Combinando guards con patrones complejos
La verdadera potencia de los guards se revela cuando los combinamos con patrones estructurales complejos:
def validar_usuario(datos):
match datos:
case {"nombre": nombre, "edad": edad, "roles": roles} if edad < 18 and "admin" in roles:
return f"Error: {nombre} es menor de edad y no puede ser administrador"
case {"nombre": nombre, "edad": edad} if edad < 18:
return f"{nombre} es menor de edad, acceso limitado"
case {"nombre": nombre, "roles": roles} if "admin" in roles:
return f"{nombre} tiene privilegios de administrador"
case {"nombre": nombre, "email": email} if "@" in email:
return f"Usuario {nombre} con email válido"
case {"nombre": nombre}:
return f"Usuario {nombre} con datos incompletos"
case _:
return "Datos de usuario inválidos"
print(validar_usuario({"nombre": "Ana", "edad": 16, "roles": ["usuario", "admin"]}))
# Error: Ana es menor de edad y no puede ser administrador
print(validar_usuario({"nombre": "Carlos", "edad": 25, "roles": ["admin"]}))
# Carlos tiene privilegios de administrador
Captura de variables en patrones anidados
Podemos capturar variables en estructuras profundamente anidadas:
def analizar_pedido(pedido):
match pedido:
case {"cliente": {"nombre": nombre, "premium": True},
"productos": [{"id": id_producto, "precio": precio} as primer_producto, *_]} if precio > 100:
return f"Cliente premium {nombre} con producto caro ({precio}€): {primer_producto}"
case {"cliente": {"nombre": nombre}, "productos": productos} if sum(p.get("precio", 0) for p in productos) > 200:
return f"Pedido grande de {nombre}: total superior a 200€"
case {"cliente": cliente, "productos": []}:
return f"Carrito vacío para {cliente.get('nombre', 'cliente desconocido')}"
case _:
return "Formato de pedido no reconocido"
pedido = {
"cliente": {"nombre": "Laura", "premium": True, "desde": 2020},
"productos": [
{"id": "P123", "nombre": "Auriculares", "precio": 129.99},
{"id": "P456", "nombre": "Funda", "precio": 19.99}
]
}
print(analizar_pedido(pedido))
# Cliente premium Laura con producto caro (129.99€): {'id': 'P123', 'nombre': 'Auriculares', 'precio': 129.99}
Captura con comodines
Podemos usar comodines (_
) para partes del patrón que no necesitamos capturar:
def extraer_información(datos):
match datos:
case [_, segundo, *_, último]:
return f"Segundo elemento: {segundo}, último: {último}"
case {"principal": {"secundario": {"valor": valor}}, **_}:
return f"Valor anidado encontrado: {valor}"
case _:
return "Patrón no reconocido"
print(extraer_información([10, 20, 30, 40, 50]))
# Segundo elemento: 20, último: 50
print(extraer_información({"principal": {"secundario": {"valor": 42}}, "extra": "ignorado"}))
# Valor anidado encontrado: 42
Captura con tipos específicos
Podemos combinar la captura de variables con verificación de tipos:
def procesar_valor(valor):
match valor:
case str() as texto if texto.isdigit():
return f"Texto numérico: {texto}"
case str() as texto:
return f"Texto no numérico: {texto}"
case int() as numero if numero % 2 == 0:
return f"Número par: {numero}"
case int() as numero:
return f"Número impar: {numero}"
case list() as lista if all(isinstance(x, int) for x in lista):
return f"Lista de enteros: {lista}"
case _:
return "Tipo no manejado"
print(procesar_valor("123")) # Texto numérico: 123
print(procesar_valor("abc")) # Texto no numérico: abc
print(procesar_valor(42)) # Número par: 42
print(procesar_valor([1, 2, 3])) # Lista de enteros: [1, 2, 3]
Captura en clases con atributos dinámicos
Podemos capturar valores de atributos en clases que los definen dinámicamente:
class Configuración:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def validar_configuración(config):
match config:
case Configuración(debug=True, entorno=entorno) if entorno != "producción":
return f"Modo debug activado en entorno {entorno}"
case Configuración(timeout=timeout) if timeout < 100:
return f"Advertencia: timeout demasiado bajo ({timeout}ms)"
case Configuración(max_usuarios=max_usuarios, conexiones=conexiones) if conexiones > max_usuarios:
return f"Error: {conexiones} conexiones exceden el límite de {max_usuarios} usuarios"
case _:
return "Configuración válida"
print(validar_configuración(Configuración(debug=True, entorno="desarrollo")))
# Modo debug activado en entorno desarrollo
print(validar_configuración(Configuración(timeout=50, host="localhost")))
# Advertencia: timeout demasiado bajo (50ms)
Buenas prácticas con guards y captura de variables
- Orden de los casos: Coloca los patrones más específicos antes que los más generales, ya que Python evalúa los casos en orden secuencial.
def analizar_número(n):
match n:
case int() as x if x == 0: # Caso específico primero
return "Cero"
case int() as x if x > 0: # Caso más general después
return "Positivo"
case int(): # Caso aún más general
return "Negativo"
case _:
return "No es un entero"
- Evita efectos secundarios en guards: Los guards deben ser expresiones puras sin efectos secundarios.
# Incorrecto (efectos secundarios en guard)
case datos if print("Evaluando...") or len(datos) > 10:
return "Muchos datos"
# Correcto
case datos:
print("Evaluando...")
if len(datos) > 10:
return "Muchos datos"
- Usa nombres descriptivos para las variables capturadas:
# Poco descriptivo
case {"usuario": u, "permisos": p} if "admin" in p:
return f"{u} es administrador"
# Más descriptivo
case {"usuario": nombre_usuario, "permisos": permisos} if "admin" in permisos:
return f"{nombre_usuario} es administrador"
- Combina captura con desempaquetado cuando sea apropiado:
def procesar_coordenadas(datos):
match datos:
case {"puntos": [*puntos]} if len(puntos) >= 3:
x1, y1 = puntos[0]
x2, y2 = puntos[1]
x3, y3 = puntos[2]
# Cálculos con las coordenadas...
return "Triángulo procesado"
Los guards y la captura de variables transforman el pattern matching de una simple herramienta de coincidencia a un poderoso mecanismo para expresar lógica compleja de manera concisa y legible. Estas características permiten escribir código que comunica claramente la intención y reduce la posibilidad de errores lógicos.
Ejercicios de esta lección Pattern matching
Evalúa tus conocimientos de esta lección Pattern matching con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Módulo math
Reto herencia
Excepciones
Introducción a Python
Reto variables
Funciones Python
Reto funciones
Módulo datetime
Reto acumulación
Reto estructuras condicionales
Polimorfismo
Módulo os
Reto métodos dunder
Diccionarios
Reto clases y objetos
Reto operadores
Operadores
Estructuras de control
Funciones lambda
Reto diccionarios
Reto función lambda
Encapsulación
Reto coleciones
Reto funciones auxiliares
Crear módulos y paquetes
Módulo datetime
Excepciones
Operadores
Diccionarios
Reto map, filter
Reto tuplas
Proyecto gestor de tareas CRUD
Tuplas
Variables
Tipos de datos
Conjuntos
Reto mixins
Módulo csv
Módulo json
Herencia
Análisis de datos de ventas con Pandas
Reto fechas y tiempo
Reto estructuras de iteración
Funciones
Reto comprehensions
Variables
Reto serialización
Módulo csv
Reto polimorfismo
Polimorfismo
Clases y objetos
Reto encapsulación
Estructuras de control
Importar módulos y paquetes
Módulo math
Funciones lambda
Reto excepciones
Listas
Reto archivos
Encapsulación
Reto conjuntos
Clases y objetos
Instalación de Python y creación de proyecto
Reto listas
Tipos de datos
Crear módulos y paquetes
Tuplas
Herencia
Reto acceso a sistema
Proyecto sintaxis calculadora
Importar módulos y paquetes
Clases y objetos
Módulo os
Listas
Conjuntos
Reto tipos de datos
Reto matemáticas
Módulo json
Todas las lecciones de Python
Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Python
Introducción
Instalación Y Creación De Proyecto
Introducción
Tema 2: Tipos De Datos, Variables Y Operadores
Introducción
Instalación De Python
Introducción
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Estructuras Control Iterativo
Sintaxis
Estructuras Control Condicional
Sintaxis
Testing Con Pytest
Sintaxis
Listas
Estructuras De Datos
Tuplas
Estructuras De Datos
Diccionarios
Estructuras De Datos
Conjuntos
Estructuras De Datos
Comprehensions
Estructuras De Datos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Mixins Y Herencia Múltiple
Programación Orientada A Objetos
Métodos Especiales (Dunder Methods)
Programación Orientada A Objetos
Composición De Clases
Programación Orientada A Objetos
Funciones Lambda
Programación Funcional
Aplicación Parcial
Programación Funcional
Entrada Y Salida, Manejo De Archivos
Programación Funcional
Decoradores
Programación Funcional
Generadores
Programación Funcional
Paradigma Funcional
Programación Funcional
Composición De Funciones
Programación Funcional
Funciones Orden Superior Map Y Filter
Programación Funcional
Funciones Auxiliares
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Archivos Comprimidos
Entrada Y Salida Io
Entrada Y Salida Avanzada
Entrada Y Salida Io
Archivos Temporales
Entrada Y Salida Io
Contexto With
Entrada Y Salida Io
Módulo Csv
Biblioteca Estándar
Módulo Json
Biblioteca Estándar
Módulo Datetime
Biblioteca Estándar
Módulo Math
Biblioteca Estándar
Módulo Os
Biblioteca Estándar
Módulo Re
Biblioteca Estándar
Módulo Random
Biblioteca Estándar
Módulo Time
Biblioteca Estándar
Módulo Collections
Biblioteca Estándar
Módulo Sys
Biblioteca Estándar
Módulo Statistics
Biblioteca Estándar
Módulo Pickle
Biblioteca Estándar
Módulo Pathlib
Biblioteca Estándar
Importar Módulos Y Paquetes
Paquetes Y Módulos
Crear Módulos Y Paquetes
Paquetes Y Módulos
Entornos Virtuales (Virtualenv, Venv)
Entorno Y Dependencias
Gestión De Dependencias (Pip, Requirements.txt)
Entorno Y Dependencias
Python-dotenv Y Variables De Entorno
Entorno Y Dependencias
Acceso A Datos Con Mysql, Pymongo Y Pandas
Acceso A Bases De Datos
Acceso A Mongodb Con Pymongo
Acceso A Bases De Datos
Acceso A Mysql Con Mysql Connector
Acceso A Bases De Datos
Novedades Python 3.13
Características Modernas
Operador Walrus
Características Modernas
Pattern Matching
Características Modernas
Instalación Beautiful Soup
Web Scraping
Sintaxis General De Beautiful Soup
Web Scraping
Tipos De Selectores
Web Scraping
Web Scraping De Html
Web Scraping
Web Scraping Para Ciencia De Datos
Web Scraping
Autenticación Y Acceso A Recursos Protegidos
Web Scraping
Combinación De Selenium Con Beautiful Soup
Web Scraping
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la sintaxis básica y uso del pattern matching con match-case en Python.
- Aprender a utilizar patrones simples, OR, captura de valores y patrones con constantes.
- Aplicar patrones estructurales para trabajar con diccionarios, secuencias y objetos personalizados.
- Utilizar guards para añadir condiciones adicionales a los patrones y capturar variables en estructuras anidadas.
- Conocer buenas prácticas y consideraciones de diseño para escribir código claro y eficiente con pattern matching.