Home > other >  different behavior for dict and list in match/case (check if it is empty or not)
different behavior for dict and list in match/case (check if it is empty or not)

Time:12-31

I know there are other ways to check if dict is empty or not using match/case (for example, dict(data) if len(data) == 0), but I can't understand why python give different answers for list and dict types while we check for emptiness

data = [1, 2]

match data:
    case []:
        print("empty")
    case [1, 2]:
        print("1, 2")
    case _:
        print("other")

# 1, 2
data = {1: 1, 2: 2}

match data:
    case {}:
        print("empty")
    case {1: 1, 2: 2}:
        print("1, 2")
    case _:
        print("other")
# empty

CodePudding user response:

There are different patterns used on the match case Python implementation. When a list or tuple is used, the pattern used is the one called Sequence Patterns. Here's an example found on the PEP:

match collection:
case 1, [x, *others]:
    print("Got 1 and a nested sequence")
case (1, x):
    print(f"Got 1 and {x}")

So let's say that we set collection as [1, 2]

The output will be:

Got 1 and 2

If we set is as [1, [2, 3]] the output will be:

Got 1 and a nested sequence

This happens because the Sequence Pattern, tries to match a given sequence on the list. So in the case you passed, it is going to try to match exactly [1, 2].

On the other hand, when a dict is used, the Mapping Patterns is the rule. If you execute this code, you will understand better.

config1 = {"router": "CNC", "network": "lan"}
config2 = {"router": "MAC"}
config3 = {"router": None}
config4 = {"network": "lan"}

configs = [config1, config2, config3, config4]

for config in configs:
    match config:
        case {"router": "MAC"}:
            print("Configuring MAC router")
        case {"router": "CNC"}:
            print("Configuring CNC router")
        case {"router": None} | {}:
            print("No router available, please check config again")

The output is going to be this:

Configuring CNC router
Configuring MAC router
No router avaiable, please cheg config again
No router avaiable, please cheg config again

So, as you can see, the code actually does not compare using == or is is actually only look on the configs, by the keys defined on the case. So if all the keys and values for the case dict can be found on the config dict, they are going to enter on that case. This happens because the idea is that you do not wanna compare the dict object itself, but check if some keys are matching with what you expecte to execute a certain action.

Basically all the keys that are present on the config dict but are not on the cases dicts is going to be ignored (not relevant for the match case)

When you put a case with an empty dict, you have no key, values, so any dict passed on the match will be accepted.

More information about this subject can be found on the official PEP:

https://peps.python.org/pep-0622/#sequence-patterns

CodePudding user response:

Because they're handled differently.

For Sequence Patterns:

A fixed-length sequence pattern fails if the length of the subject sequence is not equal to the number of subpatterns.

So clearly the length didn't match between [] and [1, 2].

But for Mapping Patterns:

A mapping pattern succeeds if every key given in the mapping pattern is present in the subject mapping, and the pattern for each key matches the corresponding item of the subject mapping.

I would like to think of it this way: because there is no key in the {} case, the condition "every key given in the mapping pattern is present in the subject mapping" is met.

Something like(only for keys' restriction):

subject = {1: 1, 2: 2}
case1 = {1: 100}
case2 = {1: 100, 2: 100}
case3 = {}
case4 = {1: 100, 3: 100}

print(case1.keys() <= subject.keys())  # True
print(case2.keys() <= subject.keys())  # True
print(case3.keys() <= subject.keys())  # True  <-- This case
print(case4.keys() <= subject.keys())  # False
  • Related