我们知道,在写Python时,使用IDE的自动补全功能,可以大大提高代码的开发效率。使用类型标注功能,可以让IDE知道应该怎么做自动补全。

当我们没有类型标注时,IDE并不知道函数的某个参数是什么东西,没有办法做补全,如下图所示。

但当我们把类型标注加上以后,IDE就能正常补全了,如下图所示:

这样做,需要从另一个文件中,把这个参数对应的类导入到当前文件里面,然后把类作为类型填写到函数参数后面。咋看起来没有什么问题,并且我,还有很多看文章的同学,应该经常这样写类型标注的代码,从而提高代码的开发效率。

但如果你的项目规模大起来以后,你就会遇到几个比较麻烦的问题:

  • 导入链过长:例如上面截图中的代码,我从model.py中导入了Detail这个类。如果我在model.py文件的开头,还有from aaa import bbb,而在aaa.py文件开头,又有from ccc import ddd;在ccc.py开头,又有from xxx import yyy……这个导入链条就会变得很长。虽然Python对模块导入已经做了缓存,多次执行from xxx import yyy时,只有第一次会生效,后面都是读取缓存,但读取缓存也会消耗一些时间。
  • 循环依赖:一般情况下,你的代码能够正常运行,那么应该是不会存在循环依赖的。否则肯定报错了。但现在你在一个原来的依赖链条之外的文件中,为了做类型标注,导入了一个已有的文件。此时有可能就会引入循环依赖。特别是当代码规模大起来以后,如果一开始没有设计好代码结构,稍不注意就会出现循环依赖。

如果你引入一个类,仅仅是为了做类型标注,那么这个问题实际上非常好解决。在Python的typing模块里面,有一个常量,叫做TYPE_CHECKING,它就是为了解决这个问题而设计的。在你使用python xxx.py来启动代码时,TYPE_CHECKING的值是False。但当IDE的类型检查或者Mypy这种静态类型检查工具运行时,TYPE_CHECKING的值是True

因此,我们可以使用下面这段代码,来提高代码的运行效率,同时规避循环依赖的问题:

1
2
3
4
5
6
7
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from xxx import YYY

def parse_detail(params: 'YYY'):
...

注意,在函数参数的类型标注里面,类YYY需要以字符串的形式写出。如下图所示:

使用这种方法,在写代码时,IDE能够正确的做自动补全。在Mypy做静态类型检查时,也能过正常通过检查。但当代码实际运行时,会自动忽略这个导入的类,从而避免对代码的运行效率造成影响。