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

如何用Mypy为Python中「非类的可调用对象」做类型注解?

问题:用类型注解限制参数为普通函数而非类

我想给以下Python函数添加精准的类型注解,缩小Callable[..., Any]的范围,让它拒绝接收类作为参数:

from typing import Callable, Any
import inspect

def foo(func: Callable[..., Any]) -> None:
    if inspect.isclass(func):
        raise ValueError
    # 执行其他操作...

class Bad:
    pass

def good() -> Bad:
    return Bad()


foo(good)  # 合法
foo(Bad)   # 非法

目前运行时可通过inspect.isclass判断,但找不到对应的静态类型注解方式,希望在静态检查阶段(如Mypy)就能阻止传入类的行为。


解决方案:自定义Protocol实现静态区分

Python标准库的typing.Callable无法区分普通函数与类(因为类本身属于可调用对象),要实现静态检查层面的限制,可通过typing.Protocol自定义专属类型:

方法1:基于Protocol定义普通函数类型

from typing import Protocol, Callable, Any, runtime_checkable
import inspect

@runtime_checkable
class RegularFunction(Protocol):
    __call__: Callable[..., Any]
    # 利用普通函数特有属性强化区分,类的构造方法无此属性
    __code__: Any

def foo(func: RegularFunction) -> None:
    if inspect.isclass(func):
        raise ValueError
    # 执行其他操作...

class Bad:
    pass

def good() -> Bad:
    return Bad()


foo(good)  # Mypy检查通过,运行正常
foo(Bad)   # Mypy触发类型错误,运行时也会抛出ValueError

方法2:结合TypeGuard实现类型窄化

如果需要更灵活的检查逻辑,可搭配TypeGuard实现静态类型推断:

from typing import TypeGuard, Callable, Any
import inspect

def is_regular_function(func: Any) -> TypeGuard[Callable[..., Any]]:
    return not inspect.isclass(func) and callable(func)

def foo(func: Any) -> None:
    if not is_regular_function(func):
        raise ValueError
    # 执行其他操作...

class Bad:
    pass

def good() -> Bad:
    return Bad()


foo(good)  # Mypy检查通过
foo(Bad)   # Mypy报错:Argument 1 to "foo" has incompatible type "Type[Bad]"; expected "Callable[..., Any]"

通过is_regular_function的类型守卫,Mypy会在静态检查阶段识别出传入类的错误。


关键说明
  • Python类型系统中,类本身属于可调用对象,因此Callable会默认包含类,无法直接排除。
  • 自定义Protocol是当前最可靠的静态检查方案,需依赖Mypy、Pyright等支持Protocol的类型检查器。
  • 运行时检查必须保留,静态检查无法覆盖所有动态生成的对象或边缘场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:22:43