Class 模式匹配错误的大小写
Class pattern is matching the wrong cases
我正在编写一个对象序列化程序,但遇到 class patterns 与预期情况不匹配的问题:
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() 时,它给出了 I-case 的无限递归迭代而不是匹配 T-case 的元组.
当我用列表子类调用 dump_obj() 时,它匹配列表的 L-case,而不是 iterables 的预期 I-case。
第一个问题:排序
这些案例并不是相互独立的。它们是从 top-down 开始测试的(就像一个长 if/elif 链),第一个匹配的获胜。
在示例中,特定匹配测试如 list、tuple 和 str 需要在 Iterable 等更一般的匹配之前出现。否则使用当前代码,像 (10, 20, 30)
这样的元组输入将匹配 I-case 而不是预期的 T-case.
第二个问题:特异性
一个class模式执行isinstance() check which would match both a type and subclasses of the type. To restrict the case to an exact match, use a type guard:
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}')
样本运行
这里我们展示了两个有问题的案例按预期工作。
>>> 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
我正在编写一个对象序列化程序,但遇到 class patterns 与预期情况不匹配的问题:
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() 时,它给出了 I-case 的无限递归迭代而不是匹配 T-case 的元组.
当我用列表子类调用 dump_obj() 时,它匹配列表的 L-case,而不是 iterables 的预期 I-case。
第一个问题:排序
这些案例并不是相互独立的。它们是从 top-down 开始测试的(就像一个长 if/elif 链),第一个匹配的获胜。
在示例中,特定匹配测试如 list、tuple 和 str 需要在 Iterable 等更一般的匹配之前出现。否则使用当前代码,像 (10, 20, 30)
这样的元组输入将匹配 I-case 而不是预期的 T-case.
第二个问题:特异性
一个class模式执行isinstance() check which would match both a type and subclasses of the type. To restrict the case to an exact match, use a type guard:
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}')
样本运行
这里我们展示了两个有问题的案例按预期工作。
>>> 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