Python 3.5类型注解变量无初值
Python 3.5 type annotated variable without initial value
我需要声明 "complex" 类型的全局变量,并且 不应 在导入时实例化。在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: error: Incompatible types in assignment (expression has type "ellipsis", variable has type "Logger")
当然我可以使用Optional
类型来允许初始化为None
,但是类型检查会被削弱。例如,在代码的其他地方给变量赋值 None
是非法的,但不会被捕获。
是否有可接受的方法以与 Python 3.5 兼容的方式对变量进行强类型检查?
您可以采用的一种技术是创建类型为 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 一样被 type-checkers 视为 True。
(if False
也有效。这是一个 common-enough 约定,mypy 直接支持它作为使用 TYPE_CHECKING
的替代方法。)
根据PEP 484,分配None
是正确的。
log = None # type: logging.Logger
注意 mypy
only allows this at class scope. However, you can declare a type and tell mypy
to ignore the assignment itself (since mypy
0.700).
log = None # type: logging.Logger # type: ignore
此外,无论 Python 版本如何,您都可以使用 .pyi
存根文件。
# my_lib.pyi
log: logging.Logger
However, in non-stub code for versions of Python 3.5 and earlier there is a special case:
from typing import IO
stream = None # type: IO[str]
Type checkers should not complain about this (despite the value None not matching the given type), nor should they change the inferred type to Optional[...] (despite the rule that does this for annotated arguments with a default value of None). The assumption here is that other code will ensure that the variable is given a value of the proper type, and all uses can assume that the variable has the given type.
PEP 484 None
的特殊处理已在 python/typing#61. Sadly, the type checkers (I tried mypy and pyre) don't implement this. The problem is further discussed in python/typing#81 中决定。
有一些解决方法可以使用
- 将
None
或省略号转换为正确的动态类型。这将安抚 mypy 和 pyre。
self.process: subprocess.Popen = cast(subprocess.Popen, None)
self.process: subprocess.Popen = cast(subprocess.Popen, ...)
- 用注释抑制check,本质上和之前一样的效果,但是更好写。
self.process: subprocess.Popen = None # type: ignore
- 声明变量
Optional[...]
,然后在每次访问时检查 None
self.process: Optional[subprocess.Popen] = None
我需要声明 "complex" 类型的全局变量,并且 不应 在导入时实例化。在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: error: Incompatible types in assignment (expression has type "ellipsis", variable has type "Logger")
当然我可以使用Optional
类型来允许初始化为None
,但是类型检查会被削弱。例如,在代码的其他地方给变量赋值 None
是非法的,但不会被捕获。
是否有可接受的方法以与 Python 3.5 兼容的方式对变量进行强类型检查?
您可以采用的一种技术是创建类型为 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 一样被 type-checkers 视为 True。
(if False
也有效。这是一个 common-enough 约定,mypy 直接支持它作为使用 TYPE_CHECKING
的替代方法。)
根据PEP 484,分配None
是正确的。
log = None # type: logging.Logger
注意 mypy
only allows this at class scope. However, you can declare a type and tell mypy
to ignore the assignment itself (since mypy
0.700).
log = None # type: logging.Logger # type: ignore
此外,无论 Python 版本如何,您都可以使用 .pyi
存根文件。
# my_lib.pyi
log: logging.Logger
However, in non-stub code for versions of Python 3.5 and earlier there is a special case:
from typing import IO stream = None # type: IO[str]
Type checkers should not complain about this (despite the value None not matching the given type), nor should they change the inferred type to Optional[...] (despite the rule that does this for annotated arguments with a default value of None). The assumption here is that other code will ensure that the variable is given a value of the proper type, and all uses can assume that the variable has the given type.
PEP 484 None
的特殊处理已在 python/typing#61. Sadly, the type checkers (I tried mypy and pyre) don't implement this. The problem is further discussed in python/typing#81 中决定。
有一些解决方法可以使用
- 将
None
或省略号转换为正确的动态类型。这将安抚 mypy 和 pyre。
self.process: subprocess.Popen = cast(subprocess.Popen, None)
self.process: subprocess.Popen = cast(subprocess.Popen, ...)
- 用注释抑制check,本质上和之前一样的效果,但是更好写。
self.process: subprocess.Popen = None # type: ignore
- 声明变量
Optional[...]
,然后在每次访问时检查None
self.process: Optional[subprocess.Popen] = None