我正在尝试修复魔法和普通方法的一些方法注释。例如,我有一些案例,如:
```
class Headers(typing.Mapping[str, str]):
...
def __contains__(self, key: str) -> bool:
...
return False
def keys(self) -> typing.List[str]:
...
return ['a', 'b']
```
当我运行mypy somefile.py --disallow-untyped-defs
时,我有以下错误:
error: Argument 1 of "__contains__" incompatible with supertype "Mapping"
error: Argument 1 of "__contains__" incompatible with supertype "Container"
error: Return type of "keys" incompatible with supertype "Mapping"
我的理解是我需要使用@override
装饰器覆盖方法,我需要尊重继承的顺序。这是对的吗?
如果我的假设是正确的,那么在哪里可以找到父类的确切签名?
在询问mypy
的问题后,答案是:
子类化
typing.Mapping[str, str]
,我假设包含的参数键的函数签名应该与泛型类型匹配?
contains不是通用方法 - 它被定义为包含类型签名(self,key:object) - > bool。你可以在typeshed上查看。这样定义包含的原因是因为在{“foo”:“bar”}中执行1这样的操作在技术上是合法的。
子类化def包含(self,key)到def包含(self,key:str)在任何情况下都更具体。更具体的子类型不违反Liskov,不是吗?
当你重写一个函数时,可以使参数类型更通用,返回类型更具体。也就是说,参数类型应该是逆变的并且返回类型是协变的。
如果我们不遵守规则,我们最终可能会在代码中引入错误。例如:
class Parent:
def foo(self, x: object) -> None: ...
class Child(Parent):
def foo(self, x: str) -> None: ...
def test(x: Parent) -> None:
x.foo(300) # Safe if 'x' is actually a Parent, not safe if `x` is actually a Child.
test(Child())
因为我们打破了liskov,将一个Child实例传入测试最终引入了一个bug。
基本上,如果我在Any
上使用key
__contains__
方法是正确的,mypy
不会抱怨:
def __contains__(self, key: typing.Any) -> bool:
...
return False
你可以关注here的对话