Clase în pattern matching
Python permite utilizarea obiectelor de clasă în pattern matching ca șabloane. Să examinăm un exemplu:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def print_person(person):
match person:
case Person(name="Tom", age=37):
print("Default Person")
case Person(name=name, age=37):
print(f"Nume: {name}")
case Person(name="Tom", age=age):
print(f"Vârstă: {age}")
case Person(name=name, age=age):
print(f"Nume: {name} Vârstă: {age}")
print_person(Person("Tom", 37)) # Default person
print_person(Person("Tom", 22)) # Vârstă: 22
print_person(Person("Sam", 37)) # Nume: Sam
print_person(Person("Bob", 41)) # Nume: Bob Vârstă: 41
Aici este definită clasa Person, care, prin constructor, primește valori pentru atributele self.name și self.age.
Funcția print_person primește parametrul Person, care, așa cum se presupune, reprezintă un obiect al clasei Person. În interiorul funcției, construcția match compară valoarea parametrului person cu o serie de șabloane. Fiecare șablon reprezintă o definiție a clasei Person, în care fiecărui atribut i se asociază o anumită valoare. De exemplu, primul șablon stabilește strict valorile ambelor atribute:
case Person(name="Tom", age=37):
print("Default Person")
Acest șablon corespunde obiectului Person dacă atributul name al acestuia are valoarea "Tom", iar atributul age are valoarea 37.
Este important de menționat că acest șablon nu este un apel al constructorului Person. Șablonul doar stabilește cum se asociază atributele cu valorile.
Al doilea șablon stabilește strict valoarea doar pentru atributul age:
case Person(name=name, age=37):
print(f"Nume: {name}")
Pentru ca acest șablon să se potrivească, atributul age trebuie să fie egal cu 37, iar atributul name poate avea orice valoare. Această valoare este transmisă variabilei name. În acest caz, atât atributul, cât și variabila au aceeași valoare, dar acest lucru nu este obligatoriu, și pentru variabilă se putea folosi un alt nume, de exemplu:
case Person(name=person_name, age=37): # variabilei person_name i se transmite valoarea atributului name
print(f"Nume: {person_name}")
Al treilea șablon corespunde unui obiect Person al cărui atribut name este egal cu șirul "Tom", iar valoarea atributului age este transmisă variabilei age:
case Person(name="Tom", age=age):
print(f"Vârstă: {age}")
În ultimul șablon, atributele name și age pot avea orice valori, iar aceste valori sunt transmise variabilelor cu același nume:
case Person(name=name, age=age):
print(f"Nume: {name} Vârstă: {age}")
Nu este necesar să folosim toate atributele obiectului Person. De asemenea, putem folosi pattern-ul _ dacă trebuie să gestionăm cazuri care nu corespund niciunui șablon:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def print_person(person):
match person:
case Person(name="Tom"):
print("Default Person")
case Person(name=person_name): # obținem doar atributul name
print(f"Nume: {person_name}")
case _:
print("Nu este un Person")
print_person(Person("Tom", 37)) # Default person
print_person(Person("Sam", 37)) # Nume: Sam
print_person("Tom") # Nu este un Person
În acest caz, al doilea șablon Person(name=person_name) corespunde oricărui obiect Person, iar valoarea atributului name este transmisă variabilei person_name.
Ultimul șablon gestionează cazurile în care este transmisă o valoare care nu reprezintă un obiect Person.
Transmiterea unui set de valori
De asemenea, cu ajutorul simbolului vertical | poți defini un set de valori pe care trebuie să le aibă un atribut:
def print_person(person):
match person:
case Person(name="Tom" | "Tomas" | "Tommy"):
print("Default Person")
case Person(name=person_name): # obținem doar atributul name
print(f"Nume: {person_name}")
case _:
print("Nu este un Person")
print_person(Person("Tom", 37)) # Default person
print_person(Person("Tomas", 37)) # Default person
În acest caz, primul șablon corespunde unui obiect Person al cărui atribut name are una dintre cele trei valori: "Tom", "Tomas" sau "Tommy".
De asemenea, poți defini valori alternative pentru întregul șablon, inclusiv folosind obiecte din alte clase:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Student:
def __init__(self, name):
self.name = name
def print_person(person):
match person:
case Person(name="Tom") | Student(name="Tomas"):
print("Default Person/Student")
case Person(name=name) | Student(name=name): # obținem doar atributul name
print(f"Nume: {name}")
case _:
print("Nu este un Person sau Student")
print_person(Person("Tom", 37)) # Default Person/Student
print_person(Student("Tomas")) # Default Person/Student
print_person(Person("Bob", 41)) # Nume: Bob
print_person(Student("Mike")) # Nume: Mike
print_person("Tom") # Nu este un Person sau Student
Aici primul șablon:
case Person(name="Tom") | Student(name="Tomas")
corespunde oricărui obiect Person al cărui atribut name este "Tom" și oricărui obiect Student al cărui atribut name este "Tomas".
Al doilea șablon - case Person(name=name) | Student(name=name) corespunde oricărui obiect Person și Student.
Parametrii poziționali
În exemplele de mai sus, pentru a specifica atributele, s-a folosit numele lor: case Person(name="Tom", age=37). Însă, dacă se utilizează mai multe șabloane și în fiecare este necesar să se asocieze atributele obiectului cu anumite valori sau variabile, menționarea constantă a atributelor poate îngreuna codul. Python permite, de asemenea, utilizarea parametrilor poziționali:
class Person:
__match_args__ = ("name", "age")
def __init__(self, name, age):
self.name = name
self.age = age
def print_person(person):
match person:
case Person("Tom", 37):
print("Default Person")
case Person(person_name, 37):
print(f"Nume: {person_name}")
case Person("Tom", person_age):
print(f"Vârstă: {person_age}")
case Person(person_name, person_age):
print(f"Nume: {person_name} Vârstă: {person_age}")
print_person(Person("Tom", 37)) # Default person
print_person(Person("Tom", 22)) # Vârstă: 22
print_person(Person("Sam", 37)) # Nume: Sam
print_person(Person("Bob", 41)) # Nume: Bob Vârstă: 41
Observați în clasa Person apelul funcției:
__match_args__ = ("name", "age")
Datorită acestui fapt, Python va ști că atunci când se specifică atributele, atributul name va fi primul, iar atributul age va fi al doilea.
Astfel, în șabloane nu este necesar să se specifice numele atributului: case Person("Tom", 37) - Python va asocia atributele și valorile/variabilele pe baza poziției lor.