san*_*ash 5 python properties typing mypy
我试图让 mypy 对我的类型注释感到满意。这是最小的例子:
class FooInterface:
x: int
class FooWithAttribute(FooInterface):
x: int = 0
class FooWithProperty(FooInterface):
@property
def x(self) -> int:
return 0
Run Code Online (Sandbox Code Playgroud)
为了我的人的理解,一切都很好:既有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
Run Code Online (Sandbox Code Playgroud)
看起来不错,对吧?但是如果我们这样做会发生什么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
Run Code Online (Sandbox Code Playgroud)
为了让 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())
Run Code Online (Sandbox Code Playgroud)
不幸的是,由于mypy 中的一个错误,方法 2 还不能完全工作——mypy 不能正确理解如何使用属性覆盖属性。在这种情况下的解决方法是FooInterface.x使用 setter创建一个属性。