A **correspondência de padrões estrutural** (Structural Pattern Matching), introduzida no [[Linguagem de programação Python|Python]] 3.10 (via PEP 634), é uma poderosa declaração `match` que permite avaliar uma expressão e compará-la com uma série de padrões. Ela simplifica a lógica condicional complexa e a desestruturação de dados, tornando o código mais legível e conciso.
# Sintaxe Básica
A correspondência de padrões utiliza a declaração `match` seguida por uma série de blocos `case`.
```python
match <expressao>:
case <padrao_1>:
# Código a ser executado se a expressão corresponder ao padrao_1
case <padrao_2>:
# Código a ser executado se a expressão corresponder ao padrao_2
case _: # Padrão curinga (wildcard)
# Código a ser executado se a expressão não corresponder a nenhum dos padrões anteriores
```
* `<expressao>`: O valor que está sendo comparado.
* `<padrao>`: A estrutura ou valor com o qual a expressão é comparada.
* `_`: O padrão curinga, que corresponde a qualquer coisa e é útil para capturar casos padrão ou ignorar partes de um padrão.
# Tipos de Padrões
A correspondência de padrões é muito flexível e pode lidar com vários tipos de padrões:
1. **Padrões Literais**: Correspondem a valores exatos.
```python
status_code = 200
match status_code:
case 200:
print("OK")
case 404:
print("Não encontrado")
case _:
print("Outro status")
# Saída: OK
```
2. **Padrões de Sequência**: Desestruturam listas, tuplas e outras sequências.
```python
ponto = (10, 20)
match ponto:
case (x, y):
print(f"Ponto em x={x}, y={y}")
case _:
print("Não é um ponto 2D")
# Saída: Ponto em x=10, y=20
```
Isso é uma extensão poderosa do [[Desempacotar sequencias em Python]].
3. **Padrões de Mapeamento**: Desestruturam dicionários.
```python
usuario = {'nome': 'Alice', 'idade': 30}
match usuario:
case {'nome': n, 'idade': i}:
print(f"Nome: {n}, Idade: {i}")
case _:
print("Formato de usuário desconhecido")
# Saída: Nome: Alice, Idade: 30
```
4. **Padrões de Classe**: Correspondem a instâncias de classes e seus atributos.
```python
class Ponto:
def __init__(self, x, y):
self.x = x
self.y = y
p = Ponto(5, 10)
match p:
case Ponto(x=val_x, y=val_y):
print(f"Objeto Ponto com x={val_x}, y={val_y}")
case _:
print("Não é um objeto Ponto")
# Saída: Objeto Ponto com x=5, y=10
```
5. **Padrões de Captura**: Atribuem o valor correspondente a uma variável.
```python
valor = "hello"
match valor:
case msg: # 'msg' captura o valor de 'valor'
print(f"Capturado: {msg}")
# Saída: Capturado: hello
```
6. **Padrões `as`**: Capturam um subpadrão.
```python
coordenada = (1, 2, 3)
match coordenada:
case (x, y, z) as ponto_3d:
print(f"Ponto 3D: {ponto_3d}, x={x}, y={y}, z={z}")
# Saída: Ponto 3D: (1, 2, 3), x=1, y=2, z=3
```
7. **Padrões `OR`**: Combinam múltiplos padrões com `|`.
```python
dia = "Sábado"
match dia:
case "Sábado" | "Domingo":
print("É fim de semana!")
case _:
print("É dia de semana.")
# Saída: É fim de semana!
```
8. **Guards (`if`)**: Adicionam condições adicionais aos padrões.
```python
idade = 18
match idade:
case i if i < 18:
print("Menor de idade")
case i if i >= 18:
print("Maior de idade")
# Saída: Maior de idade
```
# Exemplos Práticos
**Exemplo 1: Processamento de Comandos Simples**
```python
def processar_comando(comando):
match comando.split():
case ["get", obj]:
print(f"Pegando o {obj}.")
case ["swing", obj]:
print(f"Balançando o {obj}! AHHHH!")
case ["abrir", "porta", direcao] if direcao in ["norte", "sul", "leste", "oeste"]:
print(f"Abrindo a porta para o {direcao}.")
case _: # Curinga para qualquer outro caso de correspondência
raise ValueError(f"Não sei como '{comando}'")
processar_comando("swing espada")
# Saída: Balançando o espada! AHHHH!
processar_comando("get chave")
# Saída: Pegando o chave.
processar_comando("abrir porta norte")
# Saída: Abrindo a porta para o norte.
# processar_comando("correr rápido")
# Levantaria ValueError: Não sei como 'correr rápido'
```
**Exemplo 2: Desestruturação de Dados Complexos com Condições**
Este exemplo demonstra como desestruturar dados aninhados e aplicar condições (`if`) para filtrar resultados, similar ao que se faria com [[Desempacotar sequencias em Python]] e lógica condicional, mas de forma mais integrada.
```python
metro_areas = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]
print(f'{"Cidade":15} | {"Latitude":>9} | {"Longitude":>9}')
print("-" * 40)
for record in metro_areas:
match record:
# Padrão de sequência: [nome, _, _, (lat, lon)]
# O '_' ignora os elementos 'código do país' e 'população'.
# O padrão aninhado '(lat, lon)' desestrutura a tupla de coordenadas.
# A guarda 'if lon <= 0' filtra apenas as cidades no hemisfério ocidental.
case [name, _, _, (lat, lon)] if lon <= 0:
print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')
case _:
# Este caso pode ser usado para lidar com outros formatos de registro
# ou para ignorar registros que não correspondem ao critério.
pass
# Saída:
# Cidade | Latitude | Longitude
# ----------------------------------------
# Mexico City | 19.4333 | -99.1333
# New York-Newark | 40.8086 | -74.0204
# São Paulo | -23.5478 | -46.6358
```
A correspondência de padrões em Python é uma ferramenta poderosa para escrever código mais limpo e expressivo, especialmente ao lidar com estruturas de dados complexas ou múltiplas condições.
:: **Referência** :: [PEP 634 – Structural Pattern Matching: Specification](https://peps.python.org/pep-0634/)