如何在Mypy中区分两个相同的Callable类型别名?
不用NewType区分同签名Callable类型别名的方法
普通的TypeAlias是结构类型——只要调用签名一致,Mypy就会认为它们可以互换。要让两个同签名的Callable类型别名在互换时触发类型错误,可通过带独特标记的Protocol实现,具体步骤如下:
1. 定义带私有标记的Protocol
给每个计算器类型创建专属的Protocol,内部加一个仅用于区分类型的私有属性(不用实际实现),这样即使调用签名完全一样,Mypy也会把它们当成不同类型:
from typing import Protocol, TypeAlias class _Calculator1Protocol(Protocol): # 私有标记,仅给类型检查器用 __calc1_marker: int def __call__(self, x: int) -> int: ... class _Calculator2Protocol(Protocol): # 另一个私有标记,和上面的区分开 __calc2_marker: int def __call__(self, x: int) -> int: ... # 用Protocol定义类型别名 Calculator1: TypeAlias = _Calculator1Protocol Calculator2: TypeAlias = _Calculator2Protocol
2. 给现有函数加类型注解
不用修改函数的实际逻辑,只要按新的类型别名标注返回值和参数即可:
def calc1(x: int) -> int: return x ** 2 def get_calc1() -> Calculator1: return calc1 # Mypy会认可,因为calc1的调用签名匹配Protocol的__call__方法 def double_application(num: int, calc: Calculator2) -> int: return calc(calc(num))
3. 测试类型检查
现在把Calculator1传入double_application,Mypy会直接抛出类型不兼容的错误:
def main(): calc = get_calc1() val = 2 # 这里Mypy会报错:Argument 2 has incompatible type "Calculator1"; expected "Calculator2" final_val = double_application(val, calc) print(final_val) main()
为啥这方法管用?
Protocol默认是结构匹配,但加了私有属性后,Mypy会把这些Protocol当成名义类型——只有明确标注为对应Protocol的对象,才会被类型检查器认可。那些私有标记不用在代码里赋值,只是给类型检查器看的“身份牌”,完全不影响代码运行。
内容的提问来源于stack exchange,提问作者Zee
相关产品推荐
相关产品推荐

