一日一技:Any与TypeVar,让IDE的自动补全更好用

语言: CN / TW / HK

摄影:产品经理

这家日料店恐怕已经倒闭了

相信有很多同学在写Python的时候,会使用类型标注来提高代码的可读性,同时还能帮助IDE实现自动补全。

假设我们现在获得了一个对象,这个对象可能是列表也可能是生成器,我写一个函数,获取它的第一个元素。代码很简单:

from typing import Iterator
from contextlib import suppress


class People:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f'{self.name}正在吃饭')

    def walk(self):
        print(f'{self.name}正在走路')


def get_first_element(ele_list):
    if isinstance(ele_list, list):
        return ele_list[0] if ele_list else None
    if isinstance(ele_list, Iterator):
        with suppress(Exception):
            value = next(ele_list)
            return value
    return None


if __name__ == '__main__':
    kingname = People('kingname')
    pm = People('pm')
    people_list = [kingname, pm]

    obj = get_first_element(people_list)
    if obj:
        print(obj.)

代码写好了,但是当我获取第一个元素,想打印它里面的数据的时候,我发现我忘记了People这个类有哪些属性了,而此时PyCharm的自动补全也失效了,我不得不把代码往回翻,去寻找People定义的位置,效率非常低。如下图所示。

如果我们使用了类型标注,就能解决这个问题:

这个常规用法,大家肯定都知道。

现在问题来了,我们除了 People 类,还有 Cat 类,并且列表里面的元素可能全是 People 类的实例,也可能全是 Cat 类实例,这种情况怎么办呢?

首先你遇到了第一个问题, get_first_element 的参数的类型标注怎么写?

你可能会写成这样:

def get_first_element(ele_list: Union[List[Union[People, Cat]], Iterator[Union[People, Cat]]])

那如果还有一个 Dog 类呢?

为了简化操作,你可能会用 Any ,类型,于是 get_first_element 变成了下面这样:

def get_first_element(ele_list: Union[List[Any], Iterator[Any]]) -> Optional[Any]:
    if isinstance(ele_list, list):
        return ele_list[0] if ele_list else None
    if isinstance(ele_list, Iterator):
        with suppress(Exception):
            value = next(ele_list)
            return value
    return None

现在你发现问题又来了,PyCharm的自动补全又坏了。因为Any是任何类型,所以在代码运行前,它其实不知道你返回的是什么东西。如下图所示:

这种情况下,你就需要使用Python类型标注中的 泛型 了。我们知道,泛型是静态语言中的概念,Python由于使用了类型标注,也有了类型。于是也就借用了这个概念。

我们来看看怎么使用它:

from typing import TypeVar

T = TypeVar('T')

注意这里的变量名 T 和TypeVar的参数 'T' 可以同时写成任意字符串,但变量名要与参数保持一致。例如:

GodType = TypeVar('GodType')

然后把T当作 Any 一样使用就可以。我们来看看效果:

可以看到,PyCharm又能自动补全了。使用 TypeVar ,可以告诉PyCharm,返回的类型跟传入参数中的 T 对应位置的类型保持一致。例如传入参数中, TList[T] 或者 Generator[T] 中,所以返回的参数需要与列表中的元素或者生成器中的元素类型保持一致。

我们用Cat生成器来测试一下,发现也能自动补全:

还有更厉害的,如果我的列表里面既有 Cat 的实例,又有 People 的实例怎么办?这个时候,PyCharm会直接把两个实例的可能补全都给你列出来:

未闻 Code·知识星球开放啦!

一对一答疑爬虫相关问题

职业生涯咨询

面试经验分享

每周直播分享

......

未闻 Code·知识星球期待与你相见~

一二线大厂在职员工

十多年码龄的编程老鸟

国内外高校在读学生

中小学刚刚入门的新人

“未闻 Code技术交流群” 等你来!

入群方式:添加微信“mekingname”,备注“粉丝群”(谢绝广告党,非诚勿扰!)

爱点赞的人,运气都不会太差