Structured pattern matching in Python 3.10

Version of the Python 3.10 , work on which began May 25, 2020, is scheduled for release October 4, 2021 and will contain a number of interesting innovations. One of the most promising innovations will be structured pattern matching (structured pattern matching). For this, a special pattern matching instruction will be introduced match



. The pattern matching functionality will undoubtedly be of interest, especially to FP programmers, where it plays an important role. The rest of the novelties of the new version of the language are described here .





Python, for all its power and popularity, for a long time did not have the form of flow control found in other languages ​​- a way to take a value and elegantly match it to one of many possible conditions. In the C and C ++ languages, this is done by a construct switch/case



; in Rust and F #, this construct is called pattern matching.





The traditional ways of doing this in Python are not elegant. One of them is to write a chain of expressions if/elif/else



. Another is in storing values ​​that are matched as keys in a dictionary, and then using the values ​​by key to perform an action - for example, storing a function as a value and using a key or some other variable as input. In many cases, these techniques work well, but are cumbersome to design and maintain.





Python , switch/case



, Python 3.10 Python : . switch/case



, .









  • Python





























Python

Python match/case



. match/case



, switch/case



. , , .





match command:
    case "quit":
        quit()
    case "reset":
        reset()
    case unknown_command:
        print (f"  '{unknown_command}')
      
      



case



, . .





Python , . Python case



, match



. case



«», case



( ).





. case



, unknown_command



, «» unknown_command, .





. case



, , . case



, .





, , . :





from enum import Enum

class Command(Enum):
    QUIT = 0
    RESET = 1

match command:
    case Command.QUIT:
        quit()
    case Command.RESET:
        reset()
      
      



; . , , , Python.





, , , , . , , , .





. , .





command = input()

match command.split():
    case [""]:
        quit()
    case ["", filename]:
        load_from(filename)
    case ["", filename]:
        save_to(filename)
    case _:
        print (f"  '{command}'")
      
      



case



:





case [""]:



, , ""



, .





case ["", filename]:



, ""



, . , filename



. case ["", filename]:



.





case _:



. , . , _



; _



match



, () ( command



case



; .)





, . :





case "a":



"a"



.





case ["a","b"]:



["a","b"]



.





case ["a", value1]:



, value1



.





case ["a", *values]:



, , . , , . , ( Python).





case ("a"|"b"|"c"):



(|



) , case



case



. "a"



, "b"



, "c"



.





case ("a"|"b"|"c") as letter:



, , letter



.





case ["a", value] if <>:



, . . , if



valid_values



, case



, .





case ["z", _]:



, "z"



.





Python , . , media_object



.jpg .





match media_object:
    case Image(type="jpg"):
        #   
        return media_object
    case Image(type="png") | Image(type="gif"):
        return render_as(media_object, "jpg")
    case Video():
        raise ValueError("     ")
    case other_type:
        raise Exception(f"  {media_object}  ")
      
      



case



, . case



Image



, "jpg"



. case



, "png"



"gif"



. case



, Video



, . case



, .





:





match media_object:
    case Image(type=media_type):
        print (f"   {media_type}")
      
      



Python , , . , , , . , .





, . , , . , if/elif/else



, , - . , - .





, if/elif/else



— ! , . , .





:





  • switch/case





# : 
#       
#  Python  3.10.

#   switch/case
def match_errno(errno):
    match errno:
        case 0:
            pass
        case 1:
            pass
        case 42:
            print("42!")
        case _:    #    
            print(" ")
      
      







#     
def command_split(command):
    match command.split():
        case ["make"]:
            print("make  ")
        case ["make", cmd]:
            print(f"  make: {cmd}")
        case ["restart"]:
            print(" ")
        case ["rm", *files]:
            print(f" : {files}")
        case _:
            print("  ")
      
      



  • (|)





#         (|)
def match_alternatives(command):
    match command.split():
        case [""] | [" ", ""]:
            print("   ")
        case ["", obj] | ["", " ", obj] | ["", obj, " "]:
            print(f" : {obj}")
      
      



  • as





#         as
def match_capture_subpattern(command):
    match command.split():
        case [" ", ("" | "" | "" | "") as direction]:
            print(f"   {direction}")
      
      



  • if





#        if
def match_guard(command, exits):
    match command.split():
        case [" ", direction] if direction in exits:
            print(f"   {direction}")
        case [" ", _]:
            print(f"     ")
      
      







#   
from dataclasses import dataclass

@dataclass
class Click:
    position: tuple[int, int]
    button: str

@dataclass
class KeyPress:
    key_name: str

@dataclass
class Quit:
    pass

def match_by_class(event):
    match event:
        case Click(position=(x,y), button="left"):
            print(f"    {x,y}")
        case Click(position=(x,y)):
            print(f"    {x,y}")
        case KeyPress("Q"|"q") | Quit():
            print("  ")
        case KeyPress(key_name="up arrow"):
            print(" ")
        case KeyPress():
            pass #    
        case other_event:
            raise ValueError(f' : {other_event}')
      
      







#   
def match_json_event(event):
    match event:
        case {"transport": "http"}:
            print(" insecure  ")
        case {"verb": "GET", "page": "articles", "pageno": n}:
            print(f"     {n}...")
        case {"verb": "POST", "page": "signup"}:
            print("   ")
      
      



:





def main():
    # x, y = 1, 2
    command_split("make")
    command_split("make clean")
    command_split("restart")
    command_split("rm a b c")
    command_split("doesnt match")
    match_errno(42)
    match_alternatives("go north")
    match_alternatives("pick up sword")
    match_capture_subpattern("go north")
    match_capture_subpattern("go east")
    match_guard("go north", exits=["east", "south"])
    match_guard("go north", exits=["north"])
    match_by_class(Click(position=(0,0), button="left"))
    match_by_class(Quit())

    try:
        match_by_class("BADVALUE")
    except ValueError:
        pass

    match_json_event({"verb": "GET", "page": "articles", "pageno": 5, "info": "extra"})
    pass

if name == 'main':     
    main()
      
      



. . Github , . «» . . , - .








All Articles