如何使用多个 @overloads (ArgumentParser.parse_args) 正确覆盖静态类型的方法
How do I correctly override a statically typed method with multiple @overloads (ArgumentParser.parse_args)
我正在尝试扩展 argparse.ArgumentParser.parse_args
,但 mypy
告诉我我做错了:
from argparse import ArgumentParser, Namespace
from typing import Sequence
class MyArgumentParser(ArgumentParser):
def parse_args(
self,
args: Sequence[str] = None,
namespace: Namespace = None,
) -> Namespace:
parsed_args = super().parse_args(args, namespace)
# process parsed_args
return parsed_args
我收到这些错误:
Signature of "parse_args" incompatible with supertype "ArgumentParser"
Superclass:
@overload
def parse_args(self, args: Optional[Sequence[str]] = ...) -> Namespace
@overload
def parse_args(self, args: Optional[Sequence[str]], namespace: None) -> Namespace
@overload
def [_N] parse_args(self, args: Optional[Sequence[str]], namespace: _N) -> _N
@overload
def parse_args(self, *, namespace: None) -> Namespace
@overload
def [_N] parse_args(self, *, namespace: _N) -> _N
Subclass:
def parse_args(self, args: Optional[Sequence[str]] = ..., namespace: Optional[Namespace] = ...) -> Namespace
我已阅读 https://mypy.readthedocs.io/en/stable/class_basics.html#overriding-statically-typed-methods,但仍有许多问题我不清楚:
这五个不同的方法签名来自哪里?我只能在这里找到一个相关的实现:
https://github.com/python/cpython/blob/f20ca766fe404a20daea29230f161a0eb71bb489/Lib/argparse.py#L1843
我必须匹配这些签名中的一个,还是全部(使用某种超集)?
为什么 namespace
不是所有签名的可选,尽管它在上面的实现中显然是可选的?
什么是_N
?
必须匹配超类的所有签名。示例:
class A:
@overload
def f(self, a: int) -> int: ...
@overload
def f(self, a: str) -> str:...
def f(self, a) -> str | int:
return a
class B(A):
# Mypy: Signature of "f" incompatible with supertype "A"
# Superclass:
# @overload def f(self, a: int) -> int
# @overload def f(self, a: str) -> str
# Subclass:
# def f(self, a: int) -> int
def f(self, a: int) -> int:
return super().f(a)
如果你确定你的签名你可以让mypy忽略
使用 type: ignore[override]
覆盖错误,仅忽略覆盖错误:
class C(A):
def f(self, a: int) -> int: # type: ignore[override]
return super().f(a)
C().f(3) # Mypy: OK
C().f('3') # Mypy: Argument 1 to "f" of "C" has incompatible type
# "str"; expected "int" [arg-type]
另一种超载解决方案:
class C(A):
@overload
def f(self, a: int) -> int: ...
@overload
def f(self, a: str) -> str:...
def f(self, a: int | str) -> int | str:
return super().f(a)
另一个 TypeVar
可以在这个简单的例子中工作:
T = TypeVar("T", int, str)
class B(A):
def f(self, a: T) -> T:
return super().f(a)
注意:您可以使用 ignore[error]
忽略每一个 Mypy 错误。
要了解 Mypy 的错误,您应该使用 --show-error-codes
.
启动 mypy
学习后
- 五个
@overload
来自 typeshed
,而不是 argparse
本身,
_N
是一个占位符,允许多次使用一个(未知)变量类型(例如,输出类型取决于输入类型),
- 可选参数不必在每个
@overload
中都是可选的,并且
- 仅匹配所有重载的 一些 超集是不够的,但需要重现最小的一组约束(感谢@hussic),
我还希望以下作品:
from argparse import ArgumentParser, Namespace
from typing import Sequence, TypeVar
_N = TypeVar("_N")
class MyArgumentParser(ArgumentParser):
def parse_args(
self, args: Sequence[str] = None, namespace: _N = None
) -> _N | Namespace:
return super().parse_args(args, namespace)
但当然不是(见下文)。
Signature of "parse_args" incompatible with supertype "ArgumentParser"
Superclass:
@overload
def parse_args(self, args: Optional[Sequence[str]] = ...) -> Namespace
@overload
def parse_args(self, args: Optional[Sequence[str]], namespace: None) -> Namespace
@overload
def [_N] parse_args(self, args: Optional[Sequence[str]], namespace: _N) -> _N
@overload
def parse_args(self, *, namespace: None) -> Namespace
@overload
def [_N] parse_args(self, *, namespace: _N) -> _N
Subclass:
def [_N] parse_args(self, args: Optional[Sequence[str]] = ..., namespace: Optional[_N] = ...) -> Union[_N, Namespace]
这是什么工作:
from argparse import ArgumentParser, Namespace
from typing import Sequence, TypeVar, overload
_N = TypeVar("_N")
class MyArgumentParser(ArgumentParser):
# https://github.com/python/typeshed/blob/494481a0/stdlib/argparse.pyi#L128-L137
@overload
def parse_args(self, args: Sequence[str] | None = ...) -> Namespace:
...
@overload
def parse_args(self, args: Sequence[str] | None, namespace: None) -> Namespace: # type: ignore[misc]
...
@overload
def parse_args(self, args: Sequence[str] | None, namespace: _N) -> _N:
...
@overload
def parse_args(self, *, namespace: None) -> Namespace: # type: ignore[misc]
...
@overload
def parse_args(self, *, namespace: _N) -> _N:
...
def parse_args(
self, args: Sequence[str] = None, namespace: _N = None
) -> _N | Namespace:
return super().parse_args(args, namespace)
不过我觉得 很小 有点太冗长了。
我想知道是否有不需要重复所有 @overload
的解决方案。
要了解为什么第一个解决方案不起作用,请考虑
"""Demonstrate bug."""
from typing import Any, TypeVar, overload
_N = TypeVar("_N", int, float)
class Base:
"""Base class."""
@overload
def fun(self, var: None) -> str:
...
@overload
def fun(self, var: _N) -> _N:
...
def fun(self, var: _N = None) -> _N | str:
"""Return var or "NaN"."""
if var is None:
return "NaN"
return var
class Class1(Base):
"""My class."""
def fun(self, var: _N = None) -> _N | str:
"""Return var or "None"."""
return super().fun(var)
class Class2(Base):
"""My class."""
@overload
def fun(self, var: None) -> str:
...
@overload
def fun(self, var: _N) -> _N:
...
def fun(self, var: _N = None) -> _N | str:
"""Return var or "None"."""
return super().fun(var)
class Class3(Base):
"""My class."""
def fun(self, var: Any = None) -> Any:
"""Return var or "None"."""
return super().fun(var)
虽然很明显 Base.fun
returns str
只有 如果 var
是 None
, Class1.fun
的签名不能说相同。因此,似乎需要复制完整的 @overload
集,例如 Class2.fun
,除非找到更聪明的解决方案(比较@hussic 的 TypeVar("T", int, str)
示例)——在这种情况下,这可能应该直接合并到对应的package/typeshed
中。 Class3.fun
是另一种总能奏效的解决方法。
我正在尝试扩展 argparse.ArgumentParser.parse_args
,但 mypy
告诉我我做错了:
from argparse import ArgumentParser, Namespace
from typing import Sequence
class MyArgumentParser(ArgumentParser):
def parse_args(
self,
args: Sequence[str] = None,
namespace: Namespace = None,
) -> Namespace:
parsed_args = super().parse_args(args, namespace)
# process parsed_args
return parsed_args
我收到这些错误:
Signature of "parse_args" incompatible with supertype "ArgumentParser"
Superclass:
@overload
def parse_args(self, args: Optional[Sequence[str]] = ...) -> Namespace
@overload
def parse_args(self, args: Optional[Sequence[str]], namespace: None) -> Namespace
@overload
def [_N] parse_args(self, args: Optional[Sequence[str]], namespace: _N) -> _N
@overload
def parse_args(self, *, namespace: None) -> Namespace
@overload
def [_N] parse_args(self, *, namespace: _N) -> _N
Subclass:
def parse_args(self, args: Optional[Sequence[str]] = ..., namespace: Optional[Namespace] = ...) -> Namespace
我已阅读 https://mypy.readthedocs.io/en/stable/class_basics.html#overriding-statically-typed-methods,但仍有许多问题我不清楚:
这五个不同的方法签名来自哪里?我只能在这里找到一个相关的实现: https://github.com/python/cpython/blob/f20ca766fe404a20daea29230f161a0eb71bb489/Lib/argparse.py#L1843
我必须匹配这些签名中的一个,还是全部(使用某种超集)?
为什么
namespace
不是所有签名的可选,尽管它在上面的实现中显然是可选的?什么是
_N
?
必须匹配超类的所有签名。示例:
class A:
@overload
def f(self, a: int) -> int: ...
@overload
def f(self, a: str) -> str:...
def f(self, a) -> str | int:
return a
class B(A):
# Mypy: Signature of "f" incompatible with supertype "A"
# Superclass:
# @overload def f(self, a: int) -> int
# @overload def f(self, a: str) -> str
# Subclass:
# def f(self, a: int) -> int
def f(self, a: int) -> int:
return super().f(a)
如果你确定你的签名你可以让mypy忽略
使用 type: ignore[override]
覆盖错误,仅忽略覆盖错误:
class C(A):
def f(self, a: int) -> int: # type: ignore[override]
return super().f(a)
C().f(3) # Mypy: OK
C().f('3') # Mypy: Argument 1 to "f" of "C" has incompatible type
# "str"; expected "int" [arg-type]
另一种超载解决方案:
class C(A):
@overload
def f(self, a: int) -> int: ...
@overload
def f(self, a: str) -> str:...
def f(self, a: int | str) -> int | str:
return super().f(a)
另一个 TypeVar
可以在这个简单的例子中工作:
T = TypeVar("T", int, str)
class B(A):
def f(self, a: T) -> T:
return super().f(a)
注意:您可以使用 ignore[error]
忽略每一个 Mypy 错误。
要了解 Mypy 的错误,您应该使用 --show-error-codes
.
学习后
- 五个
@overload
来自typeshed
,而不是argparse
本身, _N
是一个占位符,允许多次使用一个(未知)变量类型(例如,输出类型取决于输入类型),- 可选参数不必在每个
@overload
中都是可选的,并且 - 仅匹配所有重载的 一些 超集是不够的,但需要重现最小的一组约束(感谢@hussic),
我还希望以下作品:
from argparse import ArgumentParser, Namespace
from typing import Sequence, TypeVar
_N = TypeVar("_N")
class MyArgumentParser(ArgumentParser):
def parse_args(
self, args: Sequence[str] = None, namespace: _N = None
) -> _N | Namespace:
return super().parse_args(args, namespace)
但当然不是(见下文)。
Signature of "parse_args" incompatible with supertype "ArgumentParser"
Superclass:
@overload
def parse_args(self, args: Optional[Sequence[str]] = ...) -> Namespace
@overload
def parse_args(self, args: Optional[Sequence[str]], namespace: None) -> Namespace
@overload
def [_N] parse_args(self, args: Optional[Sequence[str]], namespace: _N) -> _N
@overload
def parse_args(self, *, namespace: None) -> Namespace
@overload
def [_N] parse_args(self, *, namespace: _N) -> _N
Subclass:
def [_N] parse_args(self, args: Optional[Sequence[str]] = ..., namespace: Optional[_N] = ...) -> Union[_N, Namespace]
这是什么工作:
from argparse import ArgumentParser, Namespace
from typing import Sequence, TypeVar, overload
_N = TypeVar("_N")
class MyArgumentParser(ArgumentParser):
# https://github.com/python/typeshed/blob/494481a0/stdlib/argparse.pyi#L128-L137
@overload
def parse_args(self, args: Sequence[str] | None = ...) -> Namespace:
...
@overload
def parse_args(self, args: Sequence[str] | None, namespace: None) -> Namespace: # type: ignore[misc]
...
@overload
def parse_args(self, args: Sequence[str] | None, namespace: _N) -> _N:
...
@overload
def parse_args(self, *, namespace: None) -> Namespace: # type: ignore[misc]
...
@overload
def parse_args(self, *, namespace: _N) -> _N:
...
def parse_args(
self, args: Sequence[str] = None, namespace: _N = None
) -> _N | Namespace:
return super().parse_args(args, namespace)
不过我觉得 很小 有点太冗长了。
我想知道是否有不需要重复所有 @overload
的解决方案。
要了解为什么第一个解决方案不起作用,请考虑
"""Demonstrate bug."""
from typing import Any, TypeVar, overload
_N = TypeVar("_N", int, float)
class Base:
"""Base class."""
@overload
def fun(self, var: None) -> str:
...
@overload
def fun(self, var: _N) -> _N:
...
def fun(self, var: _N = None) -> _N | str:
"""Return var or "NaN"."""
if var is None:
return "NaN"
return var
class Class1(Base):
"""My class."""
def fun(self, var: _N = None) -> _N | str:
"""Return var or "None"."""
return super().fun(var)
class Class2(Base):
"""My class."""
@overload
def fun(self, var: None) -> str:
...
@overload
def fun(self, var: _N) -> _N:
...
def fun(self, var: _N = None) -> _N | str:
"""Return var or "None"."""
return super().fun(var)
class Class3(Base):
"""My class."""
def fun(self, var: Any = None) -> Any:
"""Return var or "None"."""
return super().fun(var)
虽然很明显 Base.fun
returns str
只有 如果 var
是 None
, Class1.fun
的签名不能说相同。因此,似乎需要复制完整的 @overload
集,例如 Class2.fun
,除非找到更聪明的解决方案(比较@hussic 的 TypeVar("T", int, str)
示例)——在这种情况下,这可能应该直接合并到对应的package/typeshed
中。 Class3.fun
是另一种总能奏效的解决方法。