我曾多次遇到向现有代码添加类型注释的问题,其中抽象基类有一个方法,然后子类实现该方法并添加一些可选的关键字参数。假设代码如下所示:
class Base:
def foo(self, x: int, **kwargs) -> None: ...
class A(Base):
def foo(self, x: int, *, frob: bool = False) -> None: ...
class B(Base):
def foo(self, x: int, *, extra: str = '') -> None: ...
Mypy 将拒绝这一点,因为子类实现不如基类通用(因为它们不允许任意关键字参数)。
我可以kwargs
从基类中删除 the ,但这会导致接受任何实例Base
并通过以下方式传递任意关键字参数的函数出现问题:
def call_base(x: Base, **kwargs) -> None:
x.foo(3, **kwargs)
然后 Mypy 会抱怨,因为 Base.foo 不接受任意关键字参数。
另一种选择是让派生类实现采用 (未使用) kwargs
,但这也有缺点:
- 它不太精确,因此当实例已知(静态地)属于派生类之一时, mypy 将不太有用。
- 如果用户传递了不正确的关键字参数,它现在将被默默地忽略(除非添加额外的代码来检查这一点),而不是引发异常。
有没有什么方法可以让我鱼与熊掌兼得,即告诉 mypy 在基类上调用时不要检查附加参数,而是在派生类上这样做?人们可能认为这违反了类型模型(派生类方法无法缩小接受的参数范围),但对于单个参数来说已经可以做到这一点(将其声明为Any
在基类中,但在派生类中声明为特定类型)。