我习惯于编写类型脚本,其中可以使用!
来告诉类型检查器假设值不会为null。在python中使用类型注释时有类似的东西吗?
一个(人为的(例子:
当执行下面代码中的表达式m.maybe_num + 3
时,封闭的if
保证maybe_num
不会是None
。但类型检查器不知道这一点,并返回一个错误。(验证于https://mypy-play.net/?mypy=latest&python=3.10.(如何告诉类型检查器我更了解?
from typing import Optional
class MyClass:
def __init__(self, maybe_num: Optional[int]):
self.maybe_num = maybe_num
def has_a_num(self) -> bool:
return self.maybe_num is not None
def three_more(self) -> Optional[int]:
if self.has_a_num:
# mypy error: Unsupported operand types for + ("None" and "int")
return self.maybe_num + 3
else:
return None
遗憾的是,没有一种干净的方法可以从这样的函数调用中推断出某种东西的类型,但你可以为has_a_num()
方法使用TypeGuard
注释,尽管除非差异明显大于单个int的类型,否则这些注释的好处不会真正显现出来。如果它只是一个值,你应该只是使用一个标准不是无检查。
if self.maybe_num is not None:
...
您可以定义主子类的子类,其中任何类型受影响的参数的类型都会显式重新声明。
class MyIntClass(MyClass):
maybe_num: int
从那里,您的checker函数仍然应该返回布尔值,但带注释的返回类型告诉MyPy,它应该使用它将类型缩小到列出的类型。
遗憾的是,它只会对适当的函数参数而不是隐式self
参数执行此操作,但通过如下显式提供,可以很容易地解决此问题:
if MyClass.has_a_num(self):
...
这种语法令人讨厌,但它适用于MyPy。
这使得完整的解决方案如下
# Parse type annotations as strings to avoid
# circular class references
from __future__ import annotations
from typing import Optional, TypeGuard
class MyClass:
def __init__(self, maybe_num: Optional[int]):
self.maybe_num = maybe_num
def has_a_num(self) -> TypeGuard[_MyClass_Int]:
# This annotation defines a type-narrowing operation,
# such that if the return value is True, then self
# is (from MyPy's perspective) _MyClass_Int, and
# otherwise it isn't
return self.maybe_num is not None
def three_more(self) -> Optional[int]:
if MyClass.has_a_num(self):
# No more mypy error
return self.maybe_num + 3
else:
return None
class _MyClass_Int(MyClass):
maybe_num: int
TypeGuard
是在Python 3.10中添加的,但可以在早期版本中使用pip
中的typing_extensions
模块。