如何为同时接收泛型Factory类与Callable的函数编写类型提示?
问题描述
给定代码
from typing import Callable, Generic, TypeVar T = TypeVar("T") class Factory(Generic[T]): def __call__(self) -> T: ... class TruthyFactory(Factory[bool]): def __call__(self) -> bool: return True def falsey_factory() -> bool: return False def class_consumer(factory: type[Factory[T]]) -> T: ... def function_consumer(factory: Callable[[], T]) -> T: ... # cls_ret的类型提示为`bool` cls_ret = class_consumer(TruthyFactory) # fn_ret的类型提示为`bool` fn_ret = function_consumer(falsey_factory)
需要编写函数either_consumer的签名,使其参数兼容上述两种类型(即class_consumer与function_consumer的参数类型),签名格式为def either_consumer(factory: ???) -> T: ...。
尝试用type[Factory[T]] | Callable[[], T]作为参数类型提示,但传入Factory[T]的子类TruthyFactory时,返回类型提示错误地变为TruthyFactory而非预期的bool。推测是因为类符合Callable规范导致类型推导优先级问题,请问如何调整类型提示修正该问题?
尝试的代码
def either_consumer(factory: type[Factory[T]] | Callable[[], T]) -> T: # 需要更严谨的判断逻辑 if isinstance(factory, type): return factory()() return factory() # cls_ret的类型提示错误地为`TruthyFactory` cls_ret = either_consumer(TruthyFactory) # fn_ret的类型提示正确为`bool` fn_ret = either_consumer(falsey_factory)
解决方案
问题根源是type[Factory[T]]本身也满足Callable[[], Factory[T]]的特征,类型检查器会优先匹配Callable[[], T]分支,把T推导为TruthyFactory而非bool。以下是几种修正方案:
方案1:使用绑定类型变量区分分支
通过给TypeVar添加绑定,明确Factory子类的类型归属,让类型检查器优先识别该分支:
from typing import Callable, Generic, TypeVar, Union T = TypeVar("T") # 绑定到Factory[T],明确这是Factory子类的类型 FactoryType = TypeVar("FactoryType", bound=Factory[T]) def either_consumer(factory: Union[type[FactoryType], Callable[[], T]]) -> T: if isinstance(factory, type): return factory()() return factory() # cls_ret类型提示正确为`bool` cls_ret = either_consumer(TruthyFactory) # fn_ret类型提示仍为`bool` fn_ret = either_consumer(falsey_factory)
方案2:用协议(Protocol)定义统一行为
通过Protocol分别定义两种工厂的行为特征,消除类型歧义:
from typing import Callable, Generic, TypeVar, Protocol T = TypeVar("T") class Factory(Generic[T]): def __call__(self) -> T: ... class TruthyFactory(Factory[bool]): def __call__(self) -> bool: return True def falsey_factory() -> bool: return False # 定义直接返回T的可调用对象协议 class PlainFactoryProtocol(Protocol[T]): def __call__(self) -> T: ... # 定义返回Factory[T]的类协议 class ClassFactoryProtocol(Protocol[T]): def __call__(self) -> Factory[T]: ... def either_consumer(factory: ClassFactoryProtocol[T] | PlainFactoryProtocol[T]) -> T: if isinstance(factory, type): return factory()() return factory() # cls_ret类型提示为`bool` cls_ret = either_consumer(TruthyFactory) # fn_ret类型提示为`bool` fn_ret = either_consumer(falsey_factory)
方案3:调用时用cast临时修正(临时方案)
如果不想修改函数签名,可以在调用时用cast明确类型:
from typing import cast, type cls_ret = either_consumer(cast(type[Factory[bool]], TruthyFactory))
核心思路是让类型检查器清晰区分“返回Factory[T]的类”和“直接返回T的可调用对象”,避免因类型重叠导致的推导歧义。
内容的提问来源于stack exchange,提问作者Mardoxx
相关产品推荐
相关产品推荐

