【发布时间】:2022-11-03 16:17:46
【问题描述】:
我想将重载类型注释添加到具有如下语义的现有 API:
def f(x: Tuple[int, ...]) -> Union[int, List[int]]:
if len(x) == 1:
return x[0]
return list(x)
参数是一个元组,返回类型是 int 或 List[int] 取决于元组的长度是否为 1。
为了编码输出类型对输入类型的依赖性,我尝试了以下方法:
from typing import overload, List, Tuple
@overload
def f(x: Tuple[int]) -> int: ...
@overload
def f(x: Tuple[int, ...]) -> List[int]: ...
def f(x: Tuple[int, ...]) -> Union[int, List[int]]:
if len(x) == 1:
return x[0]
return list(x)
但是当使用 mypy 进行类型检查时,会出现以下错误:
script.py:4: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
Found 1 error in 1 file (checked 1 source file)
为了解决这个问题,我需要第二个重载有一个类型来指示“除 1 之外的任何长度的元组”,但似乎这种排他类型不存在(PEP 484: exclusive type for type hint)什么是定义的最佳方法这个函数的重载注释?
【问题讨论】:
-
你需要重载装饰器还是只做
def f(x: Tuple[int, ...]) -> int | List[int]:你会返回一个int或一个List[int]因为如果我做f(x=(1,))那仍然是一个可变长度的元组 - 即Tuple[int, ...]? -
原始函数定义本质上是这样的(我使用
Union[int, List[int]]而不是int | List[int]以兼容旧的python 版本)。问题是如何做得比这更好,以便 mypy 可以从输入中推断出正确的非联合输出类型。 -
没关系。
mypy对重载签名非常严格,正如类型理论所期望的那样。如果你之前学过 TypeScript,这对你来说可能听起来很奇怪,它几乎会吃掉任何重载组合。事实上(这是一个实现细节,但在很长一段时间内仍然如此)mypy也会按定义顺序尝试重载,因此您的代码可以按预期工作。只需将“类型:忽略”放在错误行上。外部调用者将看到您想要的界面,请参阅this gist(我的要点) -
您可能还希望
def f(x: tuple[[]]) -> NoReturn重载以防止使用空元组进行调用,如果这是您想要的。 -
谢谢 - 为了最小化复制,我删除了对零长度元组的特殊处理。关于您之前的评论,您是说推荐的方法是使用我在问题中写的内容并告诉 mypy 忽略错误吗?如果是这样,您想添加它作为答案吗?