我需要声明具有“复杂”类型的全局变量,并且不应在导入时实例化。在Python 3.6+中,我可以省略初始化,例如:
log: logging.Logger
pollset: select.poll
我需要使代码与Python 3.5兼容。我可以使用注释类型注释:
log = ... # type: logging.Logger
pollset = ... # type: select.poll
但是我必须提供初始值。在运行时这不是问题,只需分配None
或...
的初始值即可。但是其中任何一个都会触发mypy typecheck错误:
myprog.py:19: error: Incompatible types in assignment (expression has type "ellipsis", variable has type "Logger")
我当然可以使用Optional
类型来允许初始化为None
,但随后类型检查会被削弱。例如,将None
的值分配给代码中其他位置的变量是非法的,但不会被捕获。
是否存在一种可接受的方式来以与Python 3.5兼容的方式对变量进行强类型检查?
答案 0 :(得分:1)
您可以采用的一种方法是创建类型为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
。)
答案 1 :(得分:1)
根据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 [...](尽管规则对具有默认值的带注释的参数执行此操作无)。这里的假设是其他代码将确保为变量赋予适当类型的值,并且所有使用都可以假定变量具有给定类型。
答案 2 :(得分:0)
python/typing#61中决定了None
对PEP 484的特殊处理。可悲的是,类型检查器(我尝试过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