You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Python 3.13中利用PEP 646和PEP 695创建支持异构类型的可变泛型容器?

如何在Python 3.13中利用PEP 646和PEP 695创建支持异构类型的可变泛型容器?

我来帮你解决这个问题!你的思路完全正确,但在可变泛型的类型标注细节上需要调整,同时要注意类型检查器的特性支持要求。下面是修正后的完整代码和详细解释:


修正后的代码

from typing import Callable, Unpack

class Operation[R]:
    """Encapsulates a function returning type R."""
    def __init__(self, fn: Callable[[], R]) -> None:
        self._fn = fn

    def run(self) -> R:
        return self._fn()

class OperationSequence[*Ts]:
    """
    A typed-tuple container for Operation objects, 
    each possibly returning a different type in Ts.
    For example, OperationSequence[int, str] means
    item[0] returns int, item[1] returns str, etc.
    """
    def __init__(self, *ops: Unpack[tuple[Operation[*Ts]]]) -> None:
        self._ops = ops  # 类型被推断为 tuple[Operation[*Ts]]

    def __getitem__(self, index: int) -> Operation[Ts[index]]:
        return self._ops[index]

    def __len__(self) -> int:
        return len(self._ops)

    def run_all(self) -> tuple[*Ts]:
        # 正确返回与Ts对应的异构元组类型
        return tuple(op.run() for op in self._ops)

# 示例用法
def get_number() -> int:
    return 42

def get_text() -> str:
    return "Hello"

ops = OperationSequence(
    Operation(get_number),  # 对应 Ts[0] = int
    Operation(get_text),    # 对应 Ts[1] = str
)

# 现在类型检查器会正确推断:
#   ops.run_all() -> tuple[int, str]
#   ops[0].run() -> int
#   ops[1].run() -> str
print(ops.run_all())  # 输出: (42, "Hello")

关键问题修正与解释

1. 构造函数的类型标注错误

你之前写的*ops: Operation[*Ts]是错误的,因为Operation只接受单个类型参数,而*Ts是一个类型变量元组。

修正方案:使用Unpack展开由Operation[Ts[0]], Operation[Ts[1]], ...组成的元组类型,这样每个传入的Operation对象都会对应Ts中的一个具体类型,类型检查器就能正确校验传入的参数类型。

2. __getitem__的类型标注

你的思路完全正确,Ts[index]可以正确索引可变泛型中的对应类型,但需要注意:

  • 确保使用支持PEP 646的类型检查器版本(见下方注意事项)
  • 当使用字面量索引(如ops[0])时,类型检查器会精确推断出对应的Operation类型;如果使用变量索引(如i=0; ops[i]),类型检查器会返回所有可能类型的联合(如Union[Operation[int], Operation[str]]),这是合理的,因为运行时索引可能是任意整数。

注意事项

  • 类型检查器版本要求
    • mypy: 1.5+,运行时需添加选项--enable-incomplete-features=variadic-generics
    • pyright: 1.1.320+,默认支持PEP 646和PEP 695特性
  • run_all方法的返回类型tuple[*Ts]是正确的,类型检查器会自动推断为与Ts对应的异构元组类型

备注:内容来源于stack exchange,提问作者Arthur Brenno

火山引擎 最新活动