pythonliststructural-pattern-matching

match/case statement in python does not allow use of list item as the case


I have an os dependent program and I wanted to use a match/case statement and function to match a key input to its corresponding value. I have the "values" for each os stored in different tuples and I was hoping to use these as the cases by doing something like this (but in a class):

import os
import CONTROLS # File with ASCII values

# UP DOWN RIGHT LEFT
if os.name == "posix":
    CONTROL_CODES = range(65, 69)
else:
    CONTROL_CODES = (72, 80, 77, 75)

pressed = None
def _input_control_codes(char):
    global pressed
    match char:
        case CONTROL_CODES[0]: # UP
            pressed = CONTROLS.UP
            return
        case CONTROL_CODES[1]: # DOWN
            pressed = CONTROLS.DOWN
            return
        case CONTROL_CODES[2]: # RIGHT
            pressed = CONTROLS.RIGHT
            return
        case CONTROL_CODES[3]: # LEFT
            pressed = CONTROLS.LEFT
            return

_input_control_codes(65) # example
print(pressed)

The above yields the error message:

File "~/main.py", line 14
    case CONTROL_CODES[0]: # UP
                      ^
SyntaxError: invalid syntax

I was wondering if there was a way to accomplish this without having to use an if statement or manually writing 2 match/case statements?


Solution

  • Constant value patterns have to look like a sequence of one or more attribute accesses. Indexing isn't permitted.

    If you want to do this with match/case syntax, you could use a namedtuple for CONTROL_CODES:

    import collections
    import os
    import CONTROLS
    
    ControlCodes = collections.namedtuple('ControlCodes', ('UP', 'DOWN', 'RIGHT', 'LEFT'))
    
    # UP DOWN RIGHT LEFT
    if os.name == "posix":
        CONTROL_CODES = ControlCodes(*range(65, 69))
    else:
        CONTROL_CODES = ControlCodes(72, 80, 77, 75)
    
    pressed = None
    def _input_control_codes(char):
        match char:
            case CONTROL_CODES.UP:
                return CONTROLS.UP
            case CONTROL_CODES.DOWN:
                return CONTROLS.DOWN
            case CONTROL_CODES.RIGHT:
                return CONTROLS.RIGHT
            case CONTROL_CODES.LEFT:
                return CONTROLS.LEFT
            case _:
                raise ValueError(f'Invalid control code: {char}')
    
    print(_input_control_codes(65))
    

    Of course, you don't have to use match/case for this. For example, using a dict:

    import os
    import CONTROLS
    
    if os.name == 'posix':
        CONTROL_CODES = tuple(range(65, 69))
    else:
        CONTROL_CODES = (72, 80, 77, 75)
    CONTROL_MAP = dict(zip(
        CONTROL_CODES, (CONTROLS.UP, CONTROLS.DOWN, CONTROLS.RIGHT, CONTROLS.LEFT))
    
    def _input_control_codes(char):
        if char in CONTROL_MAP:
            return CONTROL_MAP[char]
        raise ValueError(f'Invalid control code: {char}')
    
    print(_input_control_codes(65))