我正在尝试改进一个组合两个相同类型值的函数,目前支持 int 和 str。这是我当前的实现:
def combine[T: (int, str)](a: T, b: T) -> T:
match a:
case int():
return a + b
case str():
return a + b
这通过了 mypy 类型检查。但是,我想让它更具可扩展性,以便将来添加其他类型。我想使用类型别名 possibleTypeForCombine 来表示允许的类型。我尝试将其定义为:
possibleTypeForCombine = int | str
# or
from typing import Union
possibleTypeForCombine = Union[int, str]
我的目标是将来能够轻松地向 possibleTypeForCombine 添加新类型,并让 mypy 通知我需要在使用此类型的函数中添加新案例。 我尝试在新版本的函数中使用此类型别名:
def combine2[T: possibleTypeForCombine](a: T, b: T) -> T:
match a:
case int():
c = a + b
return c
case str():
c = a + b
return c
但是,这会导致以下 mypy 错误:
错误:缺少返回语句 [return] 错误:不支持的操作数 + 的类型(“str”和“T”)[运算符]错误:返回不兼容 值类型(得到“str”,期望“T”)[返回值]
我当然预料到了。约束泛型类型的语法是 T: (可能类型 1, 可能类型 2)。
我想知道是否可以制作类似的东西
您的代码已经具有所需的可扩展性。您可以简单地向约束元组
(int, str)
添加一个新类型,现在该函数也会检查该类型。您似乎缺少的是类型检查器在您错过添加代码来处理新类型时突出显示的能力。就目前情况而言,类型检查器已经突出显示您尚未覆盖所有基础。它将看到新类型导致您的函数到达其执行块的末尾,而无需命中 return 语句。这被报告为错误:
mytypes.py:8 error: Missing return statement [return]
如果您的代码有一个引发异常的默认分支,就会出现问题。这将掩盖编程错误。以下代码片段将通过类型检查,尽管永远无法返回
T
可能采用的新类型的值 (set
)。
def combine[T: (int, str, set)](x: T, y: T) -> T:
match x:
case int():
return x + y
case str():
return x + y
case _:
raise RuntimeError(f'Unreachable code got: {type(x)}')
assert_never()
。此函数帮助类型检查器知道一段代码永远不应该被执行。它还有助于揭示哪种类型意味着将使用 assert_never()
进行调用。最后,如果该函数被实际调用,则会引发断言错误,并显示调用它时所用参数的类型。
继续上面的例子,
set
被保留为T
可以采用的可能类型之一,然后我们得到匹配语句的默认分支来调用assert_never()
。
from typing import assert_never
def combine[T: (int, str, set)](x: T, y: T) -> T:
match x:
case int():
return x + y
case str():
return x + y
# case set():
# return x | y
case _ as unreachable:
assert_never(unreachable)
就目前情况而言,mypy 将报告以
assert_never(unreachable)
类型调用 set[Any]
,并显示以下消息:
mytypes.py:12: error: Argument 1 to "assert_never" has incompatible type "set[Any]"; expected "Never" [arg-type]
如果被注释的代码被取消注释,则该函数将再次处理所有类型并通过类型检查。
您应该使用带有约束的
TypeVar
而不是内联泛型
from typing import TypeVar
T = TypeVar("T", int, str)
def combine2(a: T, b: T) -> T:
match a:
case int():
c = a + b
return c
case str():
c = a + b
return c