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())
Подход 2, к сожалению, пока не совсем работает из-за ошибки в mypy - mypy не правильнопонять, как обрабатывать переопределение атрибута свойством. Обходной путь в этом случае должен сделать FooInterface.x
свойством с установщиком.