我一直在包的
.py
文件中添加类型信息,以支持针对包运行 mypy
。除此之外,还允许为此第三方包生成 [typeshed][1] 信息。
由于我的包必须与 Python 2.7 兼容,因此我对类型信息使用注释:
def __init__(self, s):
# type: (Text) -> None
但是为了运行
mypy
,这需要我导入输入:
from typing import Text, IO, BinaryIO, Union
这会导致两个问题:
这不适用于 Python 3.5.0 和 3.5.1,因为它有一个模块
typing
但不包括 Text
。从 PyPI 安装 [typing
][2] 并不能解决这个问题。 (并且有些用户在该版本的 Python 上运行该包)。
这使得我的软件包依赖于[
typing
][2]进行2.7/3.3/3.4安装,需要额外的下载和安装。
我定义了自己的
Union
类型:
StreamType = Union[BinaryIO, IO[str], StringIO]
StreamTextType = Union[Text, StreamType]
此代码必须根据输入是否可用而有条件地执行。
对于第一个问题,由于我不在Python 3.5.0/1下运行
mypy
,我可以这样做:
import sys
if sys.version_info < (3, 5, 0) and sys.version_info >= (3, 5, 2):
from typing import Text, IO, BinaryIO, Union
但这并不能解决第二个问题。
注释掉
import
,就像注释中的类型信息一样,
# from typing import Text, IO, BinaryIO, Union
将导致
mypy
抛出错误 Name 'Text' is not defined
。
第三个问题可以通过使用
try
-except
(丑陋,而且可能效率也低)来解决,或者例如通过针对环境变量进行测试(这也可以用于解决第一个问题)。
运行
mypy
时是否设置了环境变量,我可以对其进行测试,以便仅在运行mypy
时执行导入语句?
针对环境变量进行测试还允许我将自己类型的定义放入“受保护”中。
或者其他解决方案? [1]:https://github.com/python/typeshed [2]:https://pypi.python.org/pypi/typing
与
mypy
关联的唯一环境变量是 MYPYPATH
,它由包的代码读取,而不是由包的代码设置。虽然 MYPYPATH
可能会被设置(特别是在生成 typeshed
信息时,以提供“其他”类型信息),但不能保证它是这样的。
您无法注释掉
import
语句,但您 可以 将其放入从不执行的块中:
if False: # MYPY
from typing import Text, IO, BinaryIO, Union
这样做的好处是,如果您在特定的 Python 文件中没有其他需要,则不必导入
os
来获取环境变量(和/或 sys
来获取 version_info
) .
您的类型定义也应该像这样指定,并且可以在导入或定义所有使用的类型之后出现在任何地方:
# import or define StringIO
if False: # MYPY
StreamType = Union[BinaryIO, IO[str], StringIO]
StreamTextType = Union[Text, StreamType]
如果上述内容位于
mytypes.py
中,则包中的任何其他源文件在任何类型定义中使用 StreamTypeText
都应该执行以下操作:
if False: # MYPY
from typing import Text, IO, BinaryIO, Union
from .mytypes StreamType
上面将满足
mypy
,因此不会因未定义Text
而抛出错误。它也适用于 3.5.0/1,并且无需使您的包依赖于 typing
如果要在 Python 2.7 环境中运行
typing
,您可能仍然需要安装 mypy
,但普通包的用户不会受此影响。
请注意,我在每个块的
# MYPY
之后添加了注释 if
。在文件中搜索 from typing
很容易,但是带有 StreamType
的块将不容易找到,以防 mypy
改变其行为并且您的代码需要调整。