作为警告,我不清楚您的代码到底是什么样子。您定义了mypy的几个不同版本,但我不确定要尝试继承哪个版本–您的问题缺少Foo。
但是我猜您正在尝试做这样的事情?
如果是这样,问题是根据基类的签名,这样做是合法的,因为class Foo:
def bar(self, *baz: float) -> str:
raise NotImplementedError
class SubFoo(Foo):
def bar(self, baz: float) -> str:
pass被定义为接受可变数量的参数。Foo.bar(...)
但是,如果我们尝试使用您的子类代替Foo,则此代码将失败,因为它仅接受一个参数。
这种认为子类应该始终能够代替父类而不引起类型错误并且不违反代码的现有前提条件和后置条件的想法被称为f = Foo()
f.bar(1, 2, 3, 4, 5, 6, 7, 8)。
但是在那种情况下,为什么要进行以下类型检查?
这是因为由于父类型的签名是class Foo:
bar: Callable[..., str]
class SubFoo(Foo):
def bar(self, baz: float) -> str:
pass,因此mypy实际上最终完全跳过了对函数参数的检查。 Callable[..., str]基本上是在说“请不要打扰检查与我的参数有关的任何内容”。
有点类似于使用...类型使动态类型与静态类型混合使用。同样,Any使您可以使用动态/不确定的签名来表示可调用项。
与以下程序进行对比:Callable[..., str]
[与上一个程序不同,此程序执行not类型检查-尽管class Foo:
def bar(self, *args: Any, **kwargs: Any) -> str:
pass
class SubFoo(Foo):
def bar(self, baz: float) -> str:
pass仍可以接受任何参数,但这种情况下参数的“结构”不灵活,mypy现在会坚持要求您子类还必须能够接受任意数量的参数。
最后,重要的一点是,此行为与是否在存根中定义了类型提示无关。相反,这全都取决于您的函数的实际类型。