W3docs

Instrução match do Python

Aprenda correspondência estrutural de padrões em Python com match/case: literais, sequências, mapeamentos, padrões de classe, guardas e curinga — com exemplos.

O Python 3.10 introduziu a correspondência estrutural de padrões por meio da instrução match — uma forma poderosa de ramificar com base na forma e no conteúdo dos dados, e não apenas na igualdade. Este capítulo aborda desde a sintaxe básica de match/case até padrões avançados como desempacotamento de sequências, padrões de mapeamento, padrões de classe, guardas e casos de uso do mundo real.

Antes de ler este capítulo, você deve estar familiarizado com if/else em Python, funções Python e estruturas de dados básicas (listas, tuplas, dicionários).

O que é Correspondência Estrutural de Padrões?

A correspondência estrutural de padrões permite inspecionar a estrutura de um objeto — seu tipo, os valores de seus campos, a forma de uma sequência — e executar código diferente dependendo de qual padrão se encaixa. Vai muito além de uma simples verificação if x == y.

Considere o roteamento de um código de status HTTP. Com cadeias de if/elif você escreve:

if status == 200:
    print("OK")
elif status == 404:
    print("Not Found")
elif status == 500:
    print("Internal Server Error")
else:
    print("Unknown status")

Com match, a intenção fica mais clara:

match status:
    case 200:
        print("OK")
    case 404:
        print("Not Found")
    case 500:
        print("Internal Server Error")
    case _:
        print("Unknown status")

A verdadeira vantagem aparece quando o sujeito é um objeto complexo — uma tupla, um dicionário ou um dataclass — e você quer desestruturá-lo enquanto faz a correspondência.

Sintaxe Básica

match subject:
    case pattern1:
        # runs if subject matches pattern1
    case pattern2:
        # runs if subject matches pattern2
    case _:
        # wildcard — runs if nothing else matched

Regras para lembrar:

  • match e case são palavras-chave suaves — elas são palavras-chave apenas neste contexto e ainda podem ser usadas como nomes de variáveis em outras partes do seu código.
  • Cada bloco case é testado em ordem; a primeira correspondência vence e as demais são ignoradas.
  • O bloco case _: é o curinga — sempre corresponde e funciona como padrão de captura de tudo.
  • É necessário Python 3.10 ou superior. Executar isso no Python 3.9 ou anterior gera um SyntaxError.

Padrões Literais

O padrão mais simples corresponde a um valor concreto: um número, uma string, True, False ou None.

def http_status(status):
    match status:
        case 200:
            return "OK"
        case 404:
            return "Not Found"
        case 500:
            return "Internal Server Error"
        case _:
            return "Unknown status"

print(http_status(200))   # OK
print(http_status(404))   # Not Found
print(http_status(999))   # Unknown status

Padrões OR (|)

Use | dentro de um case para corresponder a qualquer um de vários literais:

def is_vowel(letter):
    match letter.lower():
        case "a" | "e" | "i" | "o" | "u":
            return True
        case _:
            return False

print(is_vowel("a"))   # True
print(is_vowel("b"))   # False
print(is_vowel("E"))   # True

Os padrões OR também funcionam com números, None e outros tipos literais.

Padrões de Captura

Um padrão de captura é um nome simples (não uma string literal, não um nome com ponto) que corresponde a qualquer coisa e vincula o valor correspondido a esse nome para uso no corpo:

def greet(name):
    match name:
        case "Alice":
            return "Hello, Alice!"
        case other:          # captures whatever was passed
            return f"Hello, {other}!"

print(greet("Alice"))    # Hello, Alice!
print(greet("Bob"))      # Hello, Bob!

other acima é um padrão de captura — ele vincula o valor correspondido à variável local other. Isso se parece muito com o curinga _, mas _ descarta o valor enquanto uma captura nomeada o mantém.

Aviso

Um nome simples em um case é sempre uma captura, nunca uma comparação. Se você quiser comparar com uma constante definida em outro lugar, use um nome com ponto como Status.OK ou envolva-o em uma guarda (case x if x == my_constant:).

Padrões de Sequência

Um padrão de sequência corresponde a listas, tuplas ou qualquer sequência, e pode desestruturar os elementos em variáveis ao mesmo tempo.

def process_point(point):
    match point:
        case (0, 0):
            return "Origin"
        case (x, 0):
            return f"On x-axis at {x}"
        case (0, y):
            return f"On y-axis at {y}"
        case (x, y):
            return f"Point at ({x}, {y})"

print(process_point((0, 0)))   # Origin
print(process_point((5, 0)))   # On x-axis at 5
print(process_point((0, 3)))   # On y-axis at 3
print(process_point((2, 4)))   # Point at (2, 4)

Usando * para Capturar o Restante

Um *name dentro de um padrão de sequência coleta os elementos restantes, assim como o desempacotamento de iteráveis:

def describe_list(items):
    match items:
        case []:
            return "empty list"
        case [single]:
            return f"one item: {single}"
        case [first, *rest]:
            return f"starts with {first!r}, then {len(rest)} more item(s)"

print(describe_list([]))              # empty list
print(describe_list([42]))            # one item: 42
print(describe_list([1, 2, 3, 4]))   # starts with 1, then 3 more item(s)

Use [first, *_] se quiser capturar apenas o primeiro elemento e descartar o restante.

Padrões de Mapeamento

Um padrão de mapeamento corresponde a dicionários (ou qualquer Mapping). Você especifica apenas as chaves que lhe interessam — chaves extras no sujeito são ignoradas.

def process_event(event):
    match event:
        case {"type": "click", "button": button}:
            return f"Mouse click: button {button}"
        case {"type": "keypress", "key": key}:
            return f"Key pressed: {key!r}"
        case {"type": action}:
            return f"Other event: {action}"
        case _:
            return "Unknown event"

print(process_event({"type": "click", "button": 1}))
# Mouse click: button 1
print(process_event({"type": "keypress", "key": "Enter"}))
# Key pressed: 'Enter'
print(process_event({"type": "resize", "width": 800}))
# Other event: resize
print(process_event({}))
# Unknown event

Ponto-chave: um padrão de mapeamento nunca falha por causa de chaves extras no sujeito. {"type": "click", "button": button} corresponde mesmo que o evento também contenha coordenadas "x" e "y".

Para capturar os pares de chave/valor restantes, use **rest:

match event:
    case {"type": "click", **rest}:
        print(f"Click event with extra data: {rest}")

Padrões de Classe

Um padrão de classe corresponde a uma instância de uma classe específica e extrai seus atributos. Isso é especialmente útil com dataclasses porque seus atributos são expostos por nome automaticamente.

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

@dataclass
class Circle:
    center: Point
    radius: float

def describe_shape(shape):
    match shape:
        case Point(x=0, y=0):
            return "Point at origin"
        case Point(x=x, y=y):
            return f"Point at ({x}, {y})"
        case Circle(center=Point(x=cx, y=cy), radius=r):
            return f"Circle centered at ({cx}, {cy}) with radius {r}"
        case _:
            return "Unknown shape"

print(describe_shape(Point(0, 0)))           # Point at origin
print(describe_shape(Point(3, 4)))           # Point at (3, 4)
print(describe_shape(Circle(Point(1, 2), 5)))# Circle centered at (1, 2) with radius 5

Observe o padrão de classe aninhado no caso Circle: Point(x=cx, y=cy) é correspondido dentro do padrão Circle. Os padrões podem ser compostos com profundidade arbitrária.

Para tipos embutidos como int, str, float e bool, você pode usar padrões posicionais com um único argumento:

def handle_input(value):
    match value:
        case (int() | float()) as number:
            return f"Got a number: {number}"
        case str() as text:
            return f"Got text: {text!r}"
        case _:
            return "Unknown type"

print(handle_input(3.14))    # Got a number: 3.14
print(handle_input("hello")) # Got text: 'hello'
print(handle_input([1, 2]))  # Unknown type

A palavra-chave as (o padrão AS) vincula o valor correspondido inteiro a um nome, mesmo após uma verificação de tipo.

Guardas

Uma guarda é uma condição if adicionada após um padrão. O case só corresponde quando o padrão se encaixa e a guarda avalia como True.

def classify_number(n):
    match n:
        case 0:
            return "zero"
        case x if x < 0:
            return f"{x} is negative"
        case x if x % 2 == 0:
            return f"{x} is positive and even"
        case x:
            return f"{x} is positive and odd"

print(classify_number(0))    # zero
print(classify_number(-5))   # -5 is negative
print(classify_number(4))    # 4 is positive and even
print(classify_number(7))    # 7 is positive and odd

As guardas são avaliadas após a correspondência do padrão estrutural, então as variáveis capturadas ficam disponíveis dentro delas. Uma guarda que falha não impede que casos posteriores sejam testados.

O Padrão Curinga _

_ é o capturador universal. Ele corresponde a qualquer valor e não vincula nada (o valor é descartado). Deve ser o último case em um bloco match. Sem ele, um match que não encontrar nenhum case correspondente simplesmente não faz nada — nenhum erro é gerado.

def describe(value):
    match value:
        case 0:
            return "zero"
        case _:
            return f"something else: {value!r}"

print(describe(0))     # zero
print(describe(99))    # something else: 99
print(describe("hi"))  # something else: 'hi'

_ também pode aparecer dentro de um padrão para ignorar partes específicas:

match point:
    case (_, 0):
        print("On the x-axis (x value doesn't matter)")
    case (0, _):
        print("On the y-axis (y value doesn't matter)")

Combinando Padrões: Um Exemplo do Mundo Real

Os padrões acima se compõem. Aqui está um analisador de comandos de aventura de texto que combina padrões de sequência, guardas e o curinga:

def run_command(command):
    match command.split():
        case ["quit"]:
            return "Quitting"
        case ["go", direction] if direction in ("north", "south", "east", "west"):
            return f"Going {direction}"
        case ["go", direction]:
            return f"Cannot go {direction!r} — try north, south, east, or west"
        case ["get", item]:
            return f"Picking up {item}"
        case ["drop", item]:
            return f"Dropping {item}"
        case ["inventory"]:
            return "Checking inventory"
        case [verb, *args]:
            return f"Unknown command {verb!r} with args {args}"
        case []:
            return "No command entered"

print(run_command("go north"))    # Going north
print(run_command("go up"))       # Cannot go 'up' — try north, south, east, or west
print(run_command("get sword"))   # Picking up sword
print(run_command("drop torch"))  # Dropping torch
print(run_command("quit"))        # Quitting
print(run_command(""))            # No command entered

Lendo de cima para baixo, você pode entender imediatamente todos os comandos suportados — algo que exigiria muitas mais linhas de lógica if/elif para atingir a mesma clareza.

match vs. if/elif — Quando Usar Cada Um

CenárioMelhor escolha
Igualdade simples contra algumas constantesQualquer um; match é ligeiramente mais limpo
Correspondência na estrutura / forma dos dadosmatch — muito mais limpo
Desestruturar valores enquanto faz correspondênciamatch — não pode ser feito com if
Lógica envolvendo apenas condições calculadasif/elif
Python 3.9 ou anteriorif/elif (match não disponível)
Expressar uma tabela de decisão claramentematch

match não é substituto para toda cadeia de if. Quando todos os ramos verificam condições booleanas calculadas (por exemplo, if x > 10 and y < 5), uma cadeia de if/elif é mais natural. match brilha quando a condição diz respeito à forma dos dados.

Armadilhas Comuns

Nomes de constantes não são correspondidos pelo valor

Um nome simples em um case é sempre uma captura, nunca uma consulta:

STATUS_OK = 200

match response_code:
    case STATUS_OK:          # WRONG — this captures into STATUS_OK, not compares!
        print("Success")

Para comparar com uma constante nomeada, use um nome com ponto (http.HTTPStatus.OK) ou uma guarda:

match response_code:
    case x if x == STATUS_OK:
        print("Success")

match não é exaustivo por padrão

Ao contrário de switch em alguns outros idiomas, um match que não tem case correspondente simplesmente não faz nada silenciosamente. Adicione um case _: se precisar de um handler garantido.

match requer Python 3.10+

Executar um bloco match no Python 3.9 ou anterior gera SyntaxError: invalid syntax. Verifique sua versão com python3 --version. Consulte o guia de introdução ao Python se precisar configurar um ambiente Python moderno.

Padrões não são expressões booleanas

Você não pode escrever case x > 5: — isso é uma guarda, não um padrão. A parte estrutural (case x) deve vir primeiro, seguida de uma expressão if guard_expression opcional.

Resumo

Tipo de padrãoExemplo de sintaxeO que corresponde
Literalcase 42:Valor exato
ORcase "yes" | "y":Qualquer uma das alternativas
Curingacase _:Qualquer coisa (descarta o valor)
Capturacase x:Qualquer coisa, vincula a x
Sequênciacase [a, b, *rest]:Uma sequência com pelo menos 2 elementos
Mapeamentocase {"key": val}:Um dict contendo as chaves fornecidas
Classecase Point(x=0, y=y):Uma instância com atributos correspondentes
AScase int() as n:Corresponde e vincula o valor inteiro
Guardacase x if x > 0:Padrão + condição booleana extra

Prática

Prática
Which Python version first introduced the match statement?
Which Python version first introduced the match statement?
Prática
In a match block, what does a bare variable name in a case clause do?
In a match block, what does a bare variable name in a case clause do?
Prática
Which pattern type would you use to match a dict that contains at least a 'type' key and extract its value?
Which pattern type would you use to match a dict that contains at least a 'type' key and extract its value?

Agora que você sabe como ramificar na estrutura de dados, explore loops for em Python para iterar sobre sequências, ou enums em Python para definir o tipo de constantes tipadas que funcionam bem com padrões de classe.

Was this page helpful?