我试图让 mypy 对我的类型注释感到满意。这是一个最小的例子:
class FooInterface:
x: int
class FooWithAttribute(FooInterface):
x: int = 0
class FooWithProperty(FooInterface):
@property
def x(self) -> int:
return 0
以我人类的理解,一切都很好:
FooWithAttribute().x
和
FooWithProperty().x
都将返回
0
,即
int
,没有类型错误。然而mypy抱怨:
error: Signature of "x" incompatible with supertype "FooInterface"
有没有办法告诉mypy一切都好?现在我发现的唯一方法是在
x: typing.Any
中注释
FooInterface
,这浪费了x是int的信息。
遇到的问题是,虽然
FooWithAttribute
和
FooWithProperty
在运行时都表现出相同的行为,但在类型系统看来,它们是不同的。
mypy
正在正确地标记此差异。
以下是解决此问题的几种方法:
1. 使用
@property
装饰器定义
FooInterface
中的
x
:
from __future__ import annotations
class FooInterface:
@property
def x(self) -> int:
...
class FooWithAttribute(FooInterface):
x: int = 0
class FooWithProperty(FooInterface):
@property
def x(self) -> int:
return 0
这明确告诉
mypy
x
在
FooInterface
中应该是一个属性,从而使子类实现保持一致。
2. 使用
typing.Protocol
定义接口:
from typing import Protocol
class FooInterface(Protocol):
@property
def x(self) -> int:
...
class FooWithAttribute(FooInterface):
x: int = 0
class FooWithProperty(FooInterface):
@property
def x(self) -> int:
return 0
这利用了结构子类型(structural subtyping),
mypy
只检查接口定义的方法和属性是否存在,而不管实际的继承关系。
3. 使用类型存根文件 (
*.pyi
):
如果无法修改原始代码,可以创建一个类型存根文件来描述
FooInterface
的预期行为:
# foo_interface.pyi
class FooInterface:
@property
def x(self) -> int:
...
将此文件与的代码放在同一个目录中,
mypy
会自动使用它进行类型检查。
选择哪种方法取决于的具体需求和代码库的结构。
标签:python,python-typing,mypy From: 58349417