如何注释可以实现为 属性 的属性?
How to annotate attribute that can be implemented as property?
我正在努力让 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
都将 return 0
即 int
,没有类型错误。然而 mypy 抱怨:
error: Signature of "x" incompatible with supertype "FooInterface"
有没有办法告诉mypy一切正常?现在我发现的唯一方法是在 FooInterface
中注释 x: typing.Any
,这会浪费 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
实现 setter 以使其可写
我猜在你的情况下,你可能想采用方法 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.
我正在努力让 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
都将 return 0
即 int
,没有类型错误。然而 mypy 抱怨:
error: Signature of "x" incompatible with supertype "FooInterface"
有没有办法告诉mypy一切正常?现在我发现的唯一方法是在 FooInterface
中注释 x: typing.Any
,这会浪费 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
实现 setter 以使其可写
我猜在你的情况下,你可能想采用方法 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.