Tuplas no Pattern Matching em Python
Em Python, tuplas podem ser usadas como padrões no pattern matching. Por exemplo:
def print_data(user):
match user:
case ("Tom", 37):
print("default user")
case ("Tom", age):
print(f"Age: {age}")
case (name, 22):
print(f"Name: {name}")
case (name, age):
print(f"Name: {name} Age: {age}")
print_data(("Tom", 37)) # default user
print_data(("Tom", 28)) # Age: 28
print_data(("Sam", 22)) # Name: Sam
print_data(("Bob", 41)) # Name: Bob Age: 41
print_data(("Tom", 33, "Google")) # não corresponde a nenhum dos padrõesNesse caso, a função recebe o parâmetro user, que representa uma tupla de dois elementos. A estrutura match compara essa tupla com uma série de padrões. O primeiro padrão exige que a tupla user corresponda exatamente aos valores:
case ("Tom", 37):
print("default user")Ou seja, se o primeiro elemento da tupla for "Tom" e o segundo for 37, a mensagem "default user" será impressa no console.
O segundo padrão corresponde a qualquer tupla de dois elementos cujo primeiro elemento seja a string "Tom":
case ("Tom", age):
print(f"Age: {age}")Aqui, o segundo elemento é atribuído à variável age. Portanto, se o primeiro elemento da tupla for "Tom" e o segundo não for 37, a tupla corresponderá ao segundo padrão, e o valor do segundo elemento será armazenado em age.
O terceiro padrão é semelhante, mas agora o segundo elemento da tupla deve ser exatamente 22, e o primeiro elemento é atribuído à variável name:
case (name, 22):
print(f"Name: {name}")Se a tupla de dois elementos não corresponder aos três primeiros padrões, ela corresponderá ao quarto padrão, no qual não nos importamos com os valores específicos: eles são atribuídos às variáveis name e age:
case (name, age):
print(f"Name: {name} Age: {age}")Valores Alternativos
Se for necessário que um elemento da tupla corresponda a um conjunto de valores, esses valores podem ser listados usando o operador | (barra vertical):
def print_data(user):
match user:
case ("Tom" | "Tomas" | "Tommy", 37):
print("default user")
case ("Tom", age):
print(f"Age: {age}")
case (name, 22):
print(f"Name: {name}")
case (name, age):
print(f"Name: {name} Age: {age}")
print_data(("Tom", 37)) # default user
print_data(("Tomas", 37)) # default user
print_data(("Tom", 28)) # Age: 28
print_data(("Sam", 37)) # Name: Sam Age: 37Nesse caso, o primeiro padrão corresponde a uma tupla de dois elementos onde o primeiro elemento é "Tom", "Tomas" ou "Tommy".
Também é possível definir valores alternativos para tuplas inteiras:
def print_data(user):
match user:
case ("Tom", 37) | ("Sam", 22):
print("default user")
case (name, age):
print(f"Name: {name} Age: {age}")
print_data(("Tom", 37)) # default user
print_data(("Sam", 22)) # default user
print_data(("Mike", 28)) # Name: Mike Age: 28Aqui, o primeiro padrão corresponderá a duas tuplas específicas: ("Tom", 37) e ("Sam", 22).
Omissão de Elementos
Se não nos importamos com um elemento específico da tupla, podemos usar o caractere _ no padrão:
def print_data(user):
match user:
case ("Tom", 37):
print("default user")
case (name, _): # o segundo elemento não é importante
print(f"Name: {name}")
print_data(("Tom", 37)) # default user
print_data(("Sam", 25)) # Name: Sam
print_data(("Bob", 41)) # Name: BobPodemos usar o sublinhado para todos os elementos da tupla; nesse caso, os valores desses elementos não são importantes:
def print_data(user):
match user:
case ("Tom", 37):
print("default user")
case ("Sam", _):
print("Name: Sam")
case (_, _):
print("Undefined user")
print_data(("Tom", 37)) # default user
print_data(("Sam", 25)) # Name: Sam
print_data(([1, 2, 3], 41)) # Undefined userNote que, no último caso, o padrão (_, _) ainda corresponde apenas a tuplas de dois elementos.
No exemplo anterior, os padrões aplicados correspondiam apenas a tuplas de dois elementos. No entanto, também é possível usar padrões de tuplas com diferentes números de elementos:
def print_data(user):
match user:
case (name, age):
print(f"Name: {name} Age: {age}")
case (name, age, company):
print(f"Name: {name} Age: {age} Company: {company}")
case (name, age, company, lang):
print(f"Name: {name} Age: {age} Company: {company} Language: {lang}")
print_data(("Tom", 37)) # Name: Tom Age: 37
print_data(("Sam", 22, "Microsoft")) # Name: Sam Age: 22 Company: Microsoft
print_data(("Bob", 41, "Google", "English")) # Name: Bob Age: 41 Company: Google Language: EnglishTupla com Número Indefinido de Elementos
Se for necessário comparar uma expressão com uma tupla de comprimento indefinido, podemos usar o símbolo * para capturar os elementos restantes:
def print_data(user):
match user:
case ("Tom", 37, *rest):
print(f"Rest: {rest}")
case (name, age, *rest):
print(f"{name} ({age}): {rest}")
print_data(("Tom", 37)) # Rest: []
print_data(("Tom", 37, "Google")) # Rest: ['Google']
print_data(("Bob", 41, "Microsoft", "English")) # Bob (41): ['Microsoft', 'English']No exemplo acima, o parâmetro *rest captura todos os elementos adicionais. Os padrões ("Tom", 37, *rest) e (name, age, *rest) correspondem a qualquer tupla com dois ou mais elementos. Todos os elementos a partir do terceiro são armazenados na lista rest.
Se não nos importamos com os elementos restantes, mas ainda queremos corresponder a tuplas com um número indefinido de elementos, podemos usar *_:
def print_data(user):
match user:
case ("Tom", 37, *_):
print("Default user")
case (name, age, *_):
print(f"{name} ({age})")
print_data(("Tom", 37)) # Default user
print_data(("Tom", 37, "Google")) # Default user
print_data(("Bob", 41, "Microsoft", "English")) # Bob (41)Nesse caso, *_ indica que não estamos interessados nos elementos adicionais, mas o padrão ainda corresponderá a tuplas com dois ou mais elementos.
Documentação oficial: