带有关键字 arg 默认值的 mypy“参数的默认值不兼容”

Bra*_*mon 5 python python-3.x mypy python-typing

考虑typing.TypeVar直接来自打字文档的以下插图:

# mypytest.py
from typing import TypeVar

A = TypeVar("A", str, bytes)  # I.e. typing.AnyStr

def longest(x: A, y: A) -> A:
    """Return the longest of two strings."""
    # https://docs.python.org/3/library/typing.html
    return x if len(x) >= len(y) else y
Run Code Online (Sandbox Code Playgroud)

调用mypy mypytest.py不会引发错误并退出 0。本示例中的目的是A可以是strbytes,但返回类型将与传递的类型一致。

但是,当存在默认参数时,mypy 将引发错误:

def longest_v2(x: A = "foo", y: A = "bar") -> A:
    return x if len(x) >= len(y) else y
Run Code Online (Sandbox Code Playgroud)

提高:

$ mypy mypytest.py
mypytest.py:11: error: Incompatible default for argument "x" (default has type "str", argument has type "bytes")
mypytest.py:11: error: Incompatible default for argument "y" (default has type "str", argument has type "bytes")
Run Code Online (Sandbox Code Playgroud)

为什么在第二种情况下会发生错误?


使用行号:

  1 # mypytest.py
  2 from typing import TypeVar
  3
  4 A = TypeVar("A", str, bytes)  # I.e. typing.AnyStr
  5
  6 def longest(x: A, y: A) -> A:
  7     """Return the longest of two strings."""
  8     # https://docs.python.org/3/library/typing.html
  9     return x if len(x) >= len(y) else y
 10
 11 def longest_v2(x: A = "foo", y: A = "bar") -> A:
 12     return x if len(x) >= len(y) else y
Run Code Online (Sandbox Code Playgroud)

STe*_*kov 0

我知道这个问题很老了,但它似乎引起了足够的关注。

您描述的问题是一个众所周知的问题。这是跟踪问题

对于函数来说,这只是一个mypy限制(这就是问题仍然悬而未决的原因)。为了使事情正常运行而不忽略注释,您可以引入两个重载:

from typing import TypeVar, overload

_T = TypeVar("_T", str, bytes)  # I.e. typing.AnyStr

@overload
def longest_v2(x: str = ...) -> str: ...
@overload
def longest_v2(x: _T, y: _T) -> _T: ...
def longest_v2(x: str | bytes = 'foo', y: str | bytes = 'bar') -> str | bytes:
    return x if len(x) >= len(y) else y

reveal_type(longest_v2())  # N: revealed type is "builtins.str"
reveal_type(longest_v2('foo'))  # N: revealed type is "builtins.str"
longest_v2(b'foo')  # E: No overload variant of "longest_v2" matches argument type "bytes"  [call-overload]
reveal_type(longest_v2('foo', 'bar'))  # N: revealed type is "builtins.str"
reveal_type(longest_v2(b'foo', b'bar'))  # N: revealed type is "builtins.bytes"
Run Code Online (Sandbox Code Playgroud)

跟我玩吧!

实现签名并不重要,因为它对外部调用者不可见。第一个重载对应于带有 0 或 1 个参数的调用:第二个参数有一个默认字符串,因此应该在未给出时_T解析,这就是我们所做的。str第二个重载涵盖了提供两个参数的情况。

我认为,该功能尚未实现的原因与类中的重载有关,尤其是与类中的重载有关。在类“构造函数”__new__和中__init__,类似的行为是不安全的。问题中的示例:

from collections import OrderedDict
from typing import Generic, Mapping, TypeVar

_T1 = TypeVar('_T1', bound=Mapping)

class RawConfigParser(Generic[_T1]):
    def __init__(self, dict_type: type[_T1] = OrderedDict) -> None: ...
    def defaults(self) -> _T1: ...

class UserMapping(Mapping):
    ...

RawConfigParser[UserMapping]()  # Oops!
Run Code Online (Sandbox Code Playgroud)

这也可以修复(通过将此类调用标记为无效),但这需要在mypy实现中进行更多更改。