[我正在努力让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的信息。
Mypy实际上指出了程序中的合法错误。为了演示,假设您有一个如下所示的程序:
def mutate(f: FooInterface) -> None:
f.x = 100
似乎很好,对吧?但是,如果我们执行mutate(FooWithProperty())
,会发生什么? Python实际上会以AttributeError
崩溃!
Traceback (most recent call last):
File "test.py", line 19, in <module>
mutate(FooWithProperty())
File "test.py", line 16, in mutate
f.x = 100
AttributeError: can't set attribute
为了使Mypy开心,您基本上有两个选择:
FooInterface.x
也是只读属性FooWithProperty.x
设置可写器使其可写我猜在您的情况下,您可能希望采用方法1。如果这样做,mypy将正确指出不允许使用f.x = 100
行:
from abc import abstractmethod
class FooInterface:
# Marking this property as abstract is *optional*. If you do it,
# mypy will complain if you forget to define x in a subclass.
@property
@abstractmethod
def x(self) -> int: ...
class FooWithAttribute(FooInterface):
# No complaints from mypy here: having this attribute be writable
# won't violate the Liskov substitution principle -- it's safe to
# use FooWithAttribute in any location that expects a FooInterface.
x: int = 0
class FooWithProperty(FooInterface):
@property
def x(self) -> int:
return 0
def mutate(f: FooInterface) -> None:
# error: Property "x" defined in "FooInterface" is read-only
f.x = 100
mutate(FooWithProperty())
不幸的是,由于bug in mypy,方法2尚未完全起作用-mypy无法正确理解如何处理带有属性的属性。在这种情况下,解决方法是使FooInterface.x
具有setter的属性。