You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用mypy类型检查强制可调用类型签名?多签名参数函数校验问题

刚好我之前折腾过mypy的可调用类型约束,来给你捋清楚怎么解决这两个问题👇

解决mypy约束多签名可调用类型的问题

一、为什么isinstance(x, TYPE_A)会报错?

首先得明确:你定义的TYPE_ATYPE_B如果是用Callable写的类型别名(比如TYPE_A = Callable[[int], str]),这些都是静态类型提示,在Python运行时根本不存在对应的类。而isinstance是用来检查运行时对象的实际类型的,所以mypy会直接报错,说你传给isinstance的第二个参数不是一个合法的运行时类型。

那该怎么正确判断呢?

二、用类型守卫结合运行时签名检查

你提到的用inspect.signature判断参数个数的思路是对的,但得配合mypy的**类型守卫(Type Guard)**功能,让静态检查能识别你运行时判断后的类型。

步骤1:先明确你的可调用类型

先把两种签名的类型别名定义清楚:

from typing import Callable, TypeGuard
import inspect

# 定义两种目标可调用类型
TYPE_A = Callable[[int], str]  # 接收1个int参数,返回str
TYPE_B = Callable[[int, str], bool]  # 接收int+str参数,返回bool

步骤2:编写类型守卫函数

类型守卫函数的作用是告诉mypy:当这个函数返回True时,输入的对象肯定属于某个特定类型。这样就能把运行时的签名检查和静态类型检查打通:

def is_type_a(x: callable) -> TypeGuard[TYPE_A]:
    sig = inspect.signature(x)
    params = list(sig.parameters.values())
    # 这里可以根据需求调整检查逻辑:比如只看参数个数,或者严格检查注解类型
    return len(params) == 1 and params[0].annotation is int

def is_type_b(x: callable) -> TypeGuard[TYPE_B]:
    sig = inspect.signature(x)
    params = list(sig.parameters.values())
    return len(params) == 2 and params[0].annotation is int and params[1].annotation is str

如果你的函数参数没有显式加类型注解,那params[0].annotation会是inspect.Parameter.empty,这时候你只需要判断参数个数就行,不用检查注解。

步骤3:在业务函数中使用类型守卫

现在在你的my_func里,用这些守卫函数判断后,mypy会自动窄化变量类型,不会再报错:

def my_func(x: TYPE_A | TYPE_B) -> None:
    if is_type_a(x):
        # mypy在这里明确知道x是TYPE_A类型,调用时会检查参数是否符合
        result = x(123)
        print(f"TYPE_A执行结果: {result}")
    elif is_type_b(x):
        # 同理,这里x被识别为TYPE_B类型
        result = x(123, "测试字符串")
        print(f"TYPE_B执行结果: {result}")
    else:
        # 兜底处理,防止运行时出现不符合两种签名的函数
        raise ValueError("x必须是TYPE_A或TYPE_B类型的可调用对象")

三、额外小提示

  • 如果你用的是Python 3.9及以下版本,联合类型要写成Union[TYPE_A, TYPE_B],3.10+可以直接用TYPE_A | TYPE_B
  • 要是不想严格检查参数注解,只看参数个数,把守卫函数里的注解检查去掉就行,更灵活
  • 开启mypy的strict模式,可以确保所有分支都被覆盖,避免遗漏情况

内容的提问来源于stack exchange,提问作者user1848861

火山引擎 最新活动