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

如何为同时接收泛型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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 07:17:52