我正在嘗試擴展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?
uj5u.com熱心網友回復:
必須匹配超類的所有簽名。例子:
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 的錯誤,您應該使用--show-error-codes.
uj5u.com熱心網友回復:
學習后
- 五個
@overloads 來自typeshed,而不是來自argparse自身, - 這
_N是一個占位符,允許多次使用(未知)變數型別(例如,依賴于輸入型別的輸出型別), - 可選引數不必在每個
@overloads 中都是可選的,并且 - 僅匹配所有多載的某些超集是不夠的,還需要重現最小的一組約束(感謝@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)
不過,我覺得有點太冗長了。
我想知道是否有不需要重復所有@overloads 的解決方案。
要了解為什么第一個解決方案不起作用,請考慮
"""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)
While it is clear that Base.fun returns str only if var is None, the same cannot be said from the signature of Class1.fun. So it seems one needs to replicate the complete set of @overloads such as in Class2.fun, unless one finds a smarter solution (compare @hussic's TypeVar("T", int, str) example) - in which case this should probably be merged into the corresponding package/typeshed directly. Class3.fun is another workaround that somewhat always works.
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/428629.html
