Mechanizm dopasowywania wzorców pojawił się w Pythonie 3.10 pod nazwą Structural Pattern Matching i został zrealizowany za pomocą konstrukcji match-case. To narzędzie umożliwia elegancką i zwięzłą analizę złożonych struktur danych, porównując je z wzorcami.
W wielu językach funkcyjnych (np. w Haskellu, Scali) dopasowywanie wzorców od dawna uważane jest za wygodny sposób rozgałęzania logiki w zależności od struktury danych. Python przez długi czas nie miał takiego mechanizmu, radząc sobie za pomocą łańcuchów if-elif-else lub rozpakowywania.
Złożone zagnieżdżone struktury danych (słowniki, listy, obiekty) często wymagają wielokrotnych sprawdzeń typów, wartości i struktury, co prowadzi do chaotycznego kodu z mnóstwem warunków.
match-case zapewnia deklaratywny i czytelny sposób opisu różnych scenariuszy przetwarzania danych z uwzględnieniem typu i struktury bez zaśmiecania warunkami.
def process(event): match event: case {"type": "click", "x": x, "y": y}: return f"Kliknięcie w ({x}, {y})" case [command, *args]: return f"Komenda: {command}, argumenty: {args}" case _: return "Nieznane zdarzenie" print(process({"type": "click", "x": 2, "y": 5})) # Kliknięcie w (2, 5) print(process(["RUN", 1, 2, 3])) # Komenda: RUN, argumenty: [1, 2, 3]
Czy match-case dopasowuje tylko według wartości? Nie. Dopasowanie może zachodzić na podstawie struktury, typu, rozpakowywania kolekcji, a nawet właściwości obiektów (jeśli określono specjalne metody match_args).
Czy kolejność case w match ma znaczenie? Tak. Analiza przebiega od góry do dołu, pierwszy odpowiedni wzór jest stosowany. Kolejne case nie są sprawdzane.
Czy można dopasowywać klasy użytkownika? Tak, jeśli klasa implementuje metody match_args i/lub getitem. Wtedy dopasowywanie wzorców będzie mogło wydobywać wartości pól.
class Point: __match_args__ = ("x", "y") def __init__(self, x, y): self.x = x self.y = y def where(obj): match obj: case Point(x, y): return f"Punkt na {x}, {y}" case _: return "Nieznane" print(where(Point(1, 2))) # Punkt na 1, 2
Zalety:
Negatywny przypadek: Programiści użyli match-case nawet dla prostych rozgałęzień boolowskich, co tylko skomplikowało kod. Zalety: nauczyli się nowej funkcji. Wady: stracili czytelność i zwiększyli liczbę błędów.
Pozytywny przypadek: W aplikacji z dużą liczbą zdarzeń i zagnieżdżonymi strukturami (np. parser grafowy) match-case pozwolił uniknąć uciążliwych if-elif i scentralizować parsowanie. Zalety: zwiększona czytelność, zmniejszona liczba błędów. Wady: potrzebne było przeszkolenie zespołu w obsłudze nowej konstrukcji.