Python 3.5类型注释变量无初始值

问题描述 投票:0回答:3

我需要声明具有“复杂”类型的全局变量,并且不应该在导入时实例化。在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 兼容的方式使用变量的强类型检查?

python python-3.5 mypy python-typing
3个回答
4
投票

根据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 的带注释参数执行此操作) )。这里的假设是其他代码将确保为变量赋予正确类型的值,并且所有使用都可以假设该变量具有给定类型。



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
 的替代方案。)


0
投票

None

对PEP 484的特殊处理是在
python/typing#61中决定的。遗憾的是,类型检查器(我尝试了 mypy 和pyre)没有实现这一点。该问题在 python/typing#81 中进一步讨论。

有可以使用的解决方法

  1. 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

    声明变量
  1. Optional[...]
    ,然后在每次访问时检查 
    None
     
self.process: Optional[subprocess.Popen] = None
    
© www.soinside.com 2019 - 2024. All rights reserved.