pythonpattern-matchingstructural-pattern-matching

Clean way to check if variable is list of lists using pattern matching


In my code, I need to distinguish a list of records from a list of lists of records. The existing code does it like so:

if isinstance(input_var, list):
    if len(input_var) > 0:
        if all(isinstance(item) for item in input_var):
            return list_of_lists_processing(input_var)
        elif not any(instance(item, list) for item in input_var):
            return list_of_data_processing(input_var)
        else:
            raise ValueError(f"Unexpected input_var value {input_var}")
    else:
        return list()
else:
    raise ValueError(f"Unexpected input_var value {input_var}")

However, this seems ugly. I want to use Python 3.10's pattern matching to simplify the code. I came up with this version:

match input_var:
    case [list(), *_]:
        return list_of_lists_processing(input_var)
    case list():
        # also process empty list case
        return list_of_data_processing(input_var)
    case _:
        ValueError(f"Unexpected value {input_var=}")

But there is a flaw here: case [*list(), *_] only checks the first element of input_var, not all of them. In practice, this is enough for my purposes, but I want to ask anyway: is there a clean way to match only a list where every element is a list?

I tried case [*list()]:, but this causes a SyntaxError. case list(list()): is syntactically correct, but doesn't work as expected (for example, it matches ["a"] - what is going on here?)


Solution

  • You can match the set of item types inside your list.

    class Constants:
        set_of_list = {list}
    
    match set(type(elem) for elem in input_var):
        case Constants.set_of_list:
            return list_of_lists_processing(input_var)
        case types if list in types:
            raise ValueError(f"Unexpected input_var value {input_var}")
        case _:
            return list_of_data_processing(input_var)
    

    Python3.10 does not support matching values inside a set so you still have to check if list is one of the types.

    The Constants class is used to trigger a value pattern. Raymond Hettinger made a great talk to explain this and other concepts related to pattern matching.