If I want to match against a list containing 2 elements (1 str, 1 bool) I can do the following:

match some_lst:
    case [str(), bool()]:  # -> Valid
        do_something()

How can I apply the same logic to dictionaries without using guards? For example, this doesn't work:

match some_dict:
    case {str(): bool()}:  # -> This is invalid
        do_something()

Working example with guard:

match some_dict:
    case dict() if all(isinstance(k, str) and isinstance(v, bool) for k, v in some_dict.items()):
        do_something()  # -> This works
3

There are 3 best solutions below

2
Greg7000 On

In python, you can't easily enforce keys type and values type for your dict.

This means python has no means to verify all the keys and all the values from your statement:

match some_dict:
case {str(): bool()}:  # -> This is invalid
    do_something()

You could consider doing some kind of switch case with TypedDict, however, you can't use anystring keys with this type, you are force to define each of your keys which can often be unwanted.

Nevertheless here are 2 ways you could attack your problem:

PYTHON 3.8 with jsonschema

from contextlib import suppress
from jsonschema import ValidationError, validate

# Watch out, use json types here
# We use anystring regex for property
anystring_bool_dict = {"type": "object", "patternProperties": {"^.*$": {"type": "boolean"}}}
anystring_int_dict = {"type": "object", "patternProperties": {"^.*$": {"type": "integer"}}}

def switch_on_dict_types(case: dict):
    with suppress(ValidationError):
        if not validate(instance=case, schema=anystring_bool_dict):
            print("FIRST_CASE")
    with suppress(ValidationError):
        if not validate(instance=case, schema=anystring_int_dict):
            print("SECOND_CASE")


switch_on_dict_types({"lalal": True})
switch_on_dict_types({"lalal": 1})

# On Execution:
#    FIRST_CASE
#    SECOND_CASE

Feel free to improve indentation level and complexity from this solution if you manage to do so.

PYTHON 3.10 iterate dict keys, values and using type to string

This ain't perfect, I am just moving your loop but at least you don't run the loop for each case:

def switch_on_dict_types(dict):
    for k, v in dict.items():
        match str(type(k)) + str(type(v)):
            case "<class 'str'><class 'bool'>":
                print("string bool case")
            case "<class 'str'><class 'int'>":
                print("string int case")


switch_on_dict_types({"lalal": True})
switch_on_dict_types({"lalal": 2})

# On Execution:
#   string bool case
#   string int case
1
Raibek On

Maybe I don't get the task right but I checked and it works fine with dictionaries.

I used Python 3.10.5.

match {"a": True}:
    case {"a": True}:
        print("a: True")
    case {"a": False}:
        print("a: False")
    case {"b": True}:
        print("b: True")
    case {"b": False}:
        print("b: False")
        
a: True

match {"a": False}:
    case {"a": True}:
        print("a: True")
    case {"a": False}:
        print("a: False")
    case {"b": True}:
        print("b: True")
    case {"b": False}:
        print("b: False")
        
a: False

match {"b": True}:
    case {"a": True}:
        print("a: True")
    case {"a": False}:
        print("a: False")
    case {"b": True}:
        print("b: True")
    case {"b": False}:
        print("b: False")
        
b: True

match {"b": False}:
    case {"a": True}:
        print("a: True")
    case {"a": False}:
        print("a: False")
    case {"b": True}:
        print("b: True")
    case {"b": False}:
        print("b: False")
        
b: False
0
Thingamabobs On

A mapping pattern does not support arbitrary types, it expects a simple literal or a qualified name. However, a workaround that uses the structural pattern matching is to simply split your dictionary into a list of keys and a list of values. From here it just depends on your need if you want to check for specific identifier or rather have chunks and match key,value pairs. An working example can be found below.

class A:
    pass

class B:
    pass

def dict_typematch(subject):
    keys = list(subject)
    vals = list(subject.values())
    #chunks = [(keys[idx],vals[idx]) for idx in range(len(keys))]
    match keys:
        case str(), str():
            print('keys', 'str', 'str')
            print(vals)
            match vals:
                case str(), str():
                    print('val', 'str', 'str')
                case str(),type_ if isinstance(type_,B):
                    print('val', 'str', 'instance B')
                case a, _type if _type is A:
                    print('val', 'str', 'type A')

        case _type, str() if _type is ins_a:
            print('keys', 'str', 'ins_a')
            match vals:
                case str(), int():
                    print('vals', 'str', 'int')

        case tuple(), str():
            print('keys', 'tuple', 'str')
                    
            
        case object(), str():
            print('obj', 'str')

subject1 = {
    'a' :'hi',
    'b' :'bye'}
subject2 = { 
    'b':'hi',
    'a':B()}
subject3 = { 
    'b':'hi',
    'a':A}
dict_typematch(subject1)
dict_typematch(subject2)
dict_typematch(subject3)

ins_a = A()
subject4 = {
    ins_a:'hi',
    'a':5}
subject5 = {
    (1,A()):'hi',
    'a':B()}
dict_typematch(subject4)
dict_typematch(subject5)