You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 07:18:40