我需要声明具有“复杂”类型的全局变量,并且不应该在导入时实例化。在Python 3.6+中,我可以省略初始化,例如:
log: logging.Logger
pollset: select.poll
我需要使代码与Python 3.5兼容。我可以使用评论类型注释:
log = ... # type: logging.Logger
pollset = ... # type: select.poll
但是我必须提供初始值。这在运行时不是问题,分配
None
或 ...
的初始值就可以了。但其中任何一个都会触发 mypy 类型检查错误:
myprog.py:19:错误:赋值中的类型不兼容(表达式的类型为“省略号”,变量的类型为“Logger”)
当然我可以使用
Optional
类型来允许初始化为None
,但类型检查会被削弱。例如,在代码中的其他地方将 None
值赋给变量是非法的,但它不会被捕获。
是否有一种可接受的方法以与 Python 3.5 兼容的方式使用变量的强类型检查?
根据PEP 484,分配
None
是正确的。
log = None # type: logging.Logger
请注意,
mypy
仅在类范围内允许这样做。但是,您可以声明一个类型 并告诉 mypy
忽略赋值本身(since
mypy
0.700)。
log = None # type: logging.Logger # type: ignore
此外,无论 Python 版本如何,您都可以使用 .pyi
存根文件。
# my_lib.pyi
log: logging.Logger
但是,在 Python 3.5 及更早版本的非存根代码中,有一种特殊情况:
from typing import IO stream = None # type: IO[str]
类型检查器不应抱怨这一点(尽管值 None 与给定类型不匹配),也不应将推断类型更改为Optional[...](尽管规则对默认值为 None 的带注释参数执行此操作) )。这里的假设是其他代码将确保为变量赋予正确类型的值,并且所有使用都可以假设该变量具有给定类型。
Any
的虚拟变量,然后使用它而不是将变量设置为
...
或
None
。例如:
from typing import Any
_bogus = None # type: Any
log = _bogus # type: logging.Logger
pollset = _bogus # type: select.poll
但是,这个解决方案并不完美。使用变量注释,我们避免了实际创建并为这些变量赋值,因此在实例化之前尝试使用
log
会导致运行时出现 NameError。但是,通过这种方法,我们会得到
None
,这与我们声明的类型相矛盾。也许这适合您的用例,但如果不是,我们可以通过将它们粘贴在
if TYPE_CHECKING
块中来获得更接近变量注释行为的东西:
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
_bogus = None # type: Any
log = _bogus # type: logging.Logger
pollset = _bogus # type: select.poll
TYPE_CHECKING
变量在运行时始终为 False,但被 mypy 等类型检查器视为 True。(执行
if False
也有效。这是一个足够常见的约定,mypy 直接支持它作为使用
TYPE_CHECKING
的替代方案。)
None
对PEP 484的特殊处理是在python/typing#61中决定的。遗憾的是,类型检查器(我尝试了 mypy 和pyre)没有实现这一点。该问题在 python/typing#81 中进一步讨论。 有可以使用的解决方法
None
或省略号转换为正确的动态类型。这将安抚 mypy 和 Pyre。
self.process: subprocess.Popen = cast(subprocess.Popen, None)
self.process: subprocess.Popen = cast(subprocess.Popen, ...)
self.process: subprocess.Popen = None # type: ignore
Optional[...]
,然后在每次访问时检查
None
self.process: Optional[subprocess.Popen] = None