0

オブジェクト シリアライザーを作成していますが、クラス パターンが予想されるケースと一致しないという問題があります。

def dump_obj(x):
    match(x):
        case list():
            emit('L')
            dump_obj(len(x))
            for elem in x:
                dump_obj(elem)
        case Iterable():
            emit('I')
            dump_obj((type(x), list(x)))
        case tuple():
            emit('T')
            dump_obj(list(x))
        case str():
            emit('S')
            dump_obj(len(x))
            emit(x)
        case int():
            emit('D')
            emit(str(x))
        case _:
            raise TypeError(f'Unknown obj {x!r}')

タプルでdump_obj()を呼び出すと、タプルの T ケースに一致するのではなく、イテラブルの I ケースで無限再帰が発生します。

リストのサブクラスでdump_obj()を呼び出すと、イテラブルの意図した I ケースではなく、リストの L ケースに一致します。

4

1 に答える 1

0

最初の問題: 注文

ケースは互いに独立していません。それらはトップダウンからテストされ (長い if/elif チェーンのように)、最初に一致したものが勝ちます。

この例では、 listtuplestrなどの特定の一致テストは、Iterableなどのより一般的な一致の前に来る必要があります。それ以外の場合、現在のコードでは、タプル入力 like(10, 20, 30)は、意図した T ケースではなく I ケースに一致します。

2 番目の問題: 特異性

クラス パターンは、型とその型のサブクラスの両方に一致するisinstance()チェックを実行します。大文字と小文字を完全一致に制限するには、型ガードを使用します。

case list() if type(x) == list:
    ...

すべてを一緒に入れて

両方のソリューションを適用した新しいコードは次のとおりです。

def dump_obj(x):
    match(x):
        case list() if type(x) == list:   # <-- Added guard
            emit('L')
            dump_obj(len(x))
            for elem in x:
                dump_obj(elem)
        case tuple() if type(x) == tuple: # <-- Added guard
            emit('T')
            dump_obj(list(x))
        case str() if type(x) == str:     # <-- Added guard
            emit('S')
            dump_obj(len(x))
            emit(x)
        case Iterable():                  # <-- Move after list, tuple, str
            emit('I')
            dump_obj((type(x).__name__, list(x)))
        case int():
            emit('D')
            emit(str(x))
        case _:
            raise TypeError(f'Unknown obj {x!r}')

サンプルラン

ここでは、問題のある 2 つのケースが期待どおりに機能することを示します。

>>> dump_obj((10, 20))     # Tuple of integers
T
L
D
2
D
10
D
20

>>> class List(list):
...     pass
...
>>> dump_obj(List((30, 40)))   # List subclass
I
T
L
D
2
S
D
4
List
L
D
2
D
30
D
40
于 2022-03-01T21:25:42.780 に答える