我试图创建一个类型别名来匹配具有不同数量参数的 Callables,如下所示:
from typing import Callable
class C:
pass
type F[T1, T2] = Callable[[T1], T2] | Callable[[T1, T1], T2]
b: Callable[[C], C] = lambda a, b: a + b # error
a: Callable[[C, C], C] = lambda a, b: a + b # error
# but:
c: F[C, C] = lambda a, b: a + b # no error
但如图所示,如果我用类
C
实例化它,它就不起作用,因为它也应该给出 c
的错误。对于 int 等内置类型,它确实可以按预期工作。
我正在使用最新版本的pyright(1.1.360)
是这个吗
我已将您的代码复制到在线游乐场,以便于访问实际错误和推断类型。
让我们按顺序考虑每个陈述:
a
类型检查的定义,除了类型C
之外的预期错误之外,不支持+
运算符(Operator "+" not supported for types "C" and "C"
)。
使用
int
时,此行不会产生错误,因为 int
确实实现了 +
运算符。这可能就是为什么您会看到“像 int 这样的内置类型”和类 C
之间的差异。
b
的定义不使用Expression of type "(a: Unknown, b: Unknown) -> Unknown" is incompatible with declared type "() -> C"
进行类型检查。
这是预期的,因为带注释的指定了 0 个参数(
Callable[[], C]
中的第一个通用参数)。
c
的定义类型检查没有任何错误。
这是出乎意料的,因为
+
运算符应该产生与 a
定义中相同的错误。不幸的是,Pyright 推断出两个参数的类型为 Unknown
,其行为类似于 Any
,这意味着 +
被假定为定义良好,并且操作结果为 Unknown
,假定与 C
兼容。
除此之外,函数类型确实与注释
F[C, C]
匹配,因为 lambda 的类型被推断为 Callable[[Unknown, Unknown], Unknown]
,它与 F[C, C]
定义中的第二个选项(即 Callable[[C, C], C]
)兼容。
我相信
F
实际上代表了预期的类型,即“具有不同数量参数的可调用对象”。
这里可能让您感到困惑的是,Pyright 因使用
+
运算符而产生错误,该运算符与类型注释出现在同一行。然而,这些错误与注释本身无关。使用类型 int
时,这些错误也会消失,因为它支持 +
运算符。
我们可以进行一些实验来看看 Pyright 确实在做正确的事情:
__add__
(运算符+
)的定义添加到C
以修复a
中的错误:def C:
def __add__(self, other) -> "C":
raise NotImplemented
a: Callable[[C, C], C] = lambda a, b: a + b # no error
b
的主体以匹配其类型注释:b: Callable[[], C] = lambda: C() # no error
c
的定义中显式指定参数类型,以暴露运算符+
的错误:def c_impl(a: C, b: C) -> C:
return a + b # error: Operator "+" not supported for types "C" and "C"
c: F[C, C] = c_impl
F
的定义,不允许2参数函数产生c
错误,并证明它正在检查参数的数量:type F[T1, T2] = Callable[[T1], T2]
c: F[C, C] = lambda a, b: a + b # error
# Expression of type "(a: C, b: Unknown) -> Unknown" is incompatible with declared type "F[C, C]"
# Type "(a: C, b: Unknown) -> Unknown" is incompatible with type "F[C, C]"
# Function accepts too few positional parameters; expected 2 but received 1