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?)
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.