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

如何为Python类正确类型提示两种不同的回调函数?

解决方案:支持两种回调类型的Python类实现

核心思路

由于Protocol默认仅用于静态类型检查,运行时无法直接通过isinstance准确判断带指定参数名的回调类型。我们可以结合静态类型协议+运行时签名检查的方式解决问题:用Protocol约束API用户的回调格式,用inspect模块在运行时验证函数签名,同时缓存检查结果提升性能。

完整代码实现

from typing import Protocol, runtime_checkable, Callable
import inspect

# 定义两种回调的静态类型协议
@runtime_checkable
class NamedParamCallback(Protocol):
    def __call__(self, *, value: int) -> None:
        ...

@runtime_checkable
class NoParamCallback(Protocol):
    def __call__(self) -> None:
        ...

# 合并回调类型
CallbackType = NamedParamCallback | NoParamCallback

class CallbackHandler:
    def __init__(self, callback: CallbackType):
        self.callback = callback
        # 初始化时检查并缓存回调类型
        self._is_named_param = self._validate_named_param_callback(callback)

    def _validate_named_param_callback(self, func: Callable) -> bool:
        """检查是否是带指定关键字参数的回调"""
        sig = inspect.signature(func)
        params = list(sig.parameters.values())
        # 验证规则:仅1个关键字-only参数,名称为value,类型注解为int
        if len(params) != 1:
            return False
        param = params[0]
        return (param.name == "value" 
                and param.kind == param.KEYWORD_ONLY 
                and param.annotation is int)

    def trigger(self, condition: bool, value: int) -> None:
        """根据条件触发对应回调"""
        if condition:
            if self._is_named_param:
                self.callback(value=value)
            else:
                raise ValueError("当前条件需要带value关键字参数的回调")
        else:
            if not self._is_named_param:
                self.callback()
            else:
                raise ValueError("当前条件需要无参数的回调")

使用示例

# 符合要求的带参回调
def named_callback(*, value: int) -> None:
    print(f"带参回调触发,value={value}")

# 符合要求的无参回调
def no_param_callback() -> None:
    print("无参回调触发")

# 正常调用
handler1 = CallbackHandler(named_callback)
handler1.trigger(True, 42)  # 输出:带参回调触发,value=42

handler2 = CallbackHandler(no_param_callback)
handler2.trigger(False, 0)  # 输出:无参回调触发

# 静态检查会拦截的错误示例(无需运行即可发现)
# def bad_callback(*, num: int) -> None:
#     pass
# handler3 = CallbackHandler(bad_callback)  # mypy/pyright会提示类型不匹配

关键优势

  1. 静态类型安全:mypy、pyright等工具会严格校验回调格式,强制用户使用指定参数名和类型
  2. 运行时准确判断:通过inspect检查函数签名,避免isinstance误判问题
  3. 性能优化:初始化时缓存检查结果,避免每次触发回调都重复校验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:55:05