如何为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会提示类型不匹配
关键优势
- 静态类型安全:mypy、pyright等工具会严格校验回调格式,强制用户使用指定参数名和类型
- 运行时准确判断:通过
inspect检查函数签名,避免isinstance误判问题 - 性能优化:初始化时缓存检查结果,避免每次触发回调都重复校验
内容的提问来源于stack exchange,提问作者dbzix
相关产品推荐
相关产品推荐

