Рассмотрим следующий пример с игрушкой.
class MyClass:
def __init__(self, optional_arg: float | None = None) -> None:
self.optional_arg = optional_arg
self._parse_args()
def _parse_args(self) -> None:
if self.optional_arg is None:
self.optional_arg = 0.5
def multiply(self) -> float:
# mypy is complaining with the below statement:
# error: Unsupported operand types for * ("float" and "None") [operator]
# Right operand is of type "Optional[float]"
return 0.5 * self.optional_arg
После создания экземпляра этого класса с помощью optional_arg=None должно быть ясно, что self.optional_arg будет установлен в float из-за метода _parse_args.
На мой взгляд, должно быть ясно, что при вызове метода multiply он вернет float. Однако mypy все еще жалуется, что self.optional_arg может быть None.
Что такое питонический способ сказать mypy, что self.optional_arg не может быть None?
Self.optional_arg не обязательно должно быть значением, установленным _parse_args. Кто-то может сделать
x = MyClass()
x.optional_arg = None
x.multiply()
И что касается mypy, это допустимый способ использования MyClass.
Кроме того, кто-то может переопределить _parse_args:
class Subclass(MyClass):
def _parse_args(self) -> None:
pass
Subclass().multiply()
И что касается mypy, это тоже законно.
Если вы хотите, чтобы mypy обрабатывал self.optional_arg как никогда None, вы не должны устанавливать для него значение, которое может быть None в первую очередь. Вы можете использовать float по умолчанию в первую очередь:
class MyClass:
optional_arg: float
def __init__(self, optional_arg: float = 0.5) -> None:
self.optional_arg = optional_arg
...
Или сделайте свою None обработку, прежде чем установить атрибут:
class MyClass:
optional_arg: float
def __init__(self, optional_arg: float | None = None) -> None:
self.optional_arg = optional_arg if optional_arg is not None else 0.5
...
Ответ пользователя 2357112 отличный, и в этом случае вы должны пойти на это. Однако не всегда легко иметь правильную типизацию (особенно при работе с устаревшим кодом). В таком случае можно использовать сужение типа .
def multiply(self) -> float:
assert isinstance(self.optional_arg, float)
return 0.5 * self.optional_arg
Тоже очень полезный совет! Потрясающий.
Или сделайте def __init__(self, optional_arg: float = 0.5) -> None: и вообще исключите _parse_args.