IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    一日一技:Python类型标注的高级用法

    青南发表于 2024-11-12 13:53:53
    love 0

    假设你正在写后端代码,其中一个函数的功能是传入文章id,返回文章详情。因为项目比较大,因此在定义函数时,把类型标注加上,标明了参数的类型和返回的类型。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    from typing import List
    from dataclasses import dataclass


    @dataclass
    class ArticleDetail:
    id: int
    title: str
    content: str
    tag: List[str]


    def query_article_detail(article_id: int) -> ArticleDetail:
    if isinstance(article_id, int):
    detail = ArticleDetail(
    id=article_id,
    title='文章标题',
    content='文章内容',
    tag=['tag1', 'tag2']
    )
    return detail


    def test_query_article_detail():
    detail = query_article_detail(123)
    print(detail.content)

    现在,当你拿到返回的detail变量时,IDE的自动补全就可以正常工作了,如下图所示。

    你想让这个函数支持批量查询文章详情的功能,代码类似这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    def query_article_detail(article_id: int | List[int]) -> ArticleDetail | List[ArticleDetail]:
    if isinstance(article_id, int):
    detail = ArticleDetail(
    id=article_id,
    title='文章标题',
    content='文章内容',
    tag=['tag1', 'tag2']
    )
    return detail
    else:
    details = []
    for _id in article_id:
    detail = ArticleDetail(
    id=_id,
    title='文章标题',
    content='文章内容',
    tag=['tag1', 'tag2']
    )
    details.append(detail)
    return details

    如果传入的参数是int类型的文章id,那么就返回这篇文章的详情ArticleDetail对象。如果传入的是文章列表,那么就返回ArticleDetail对象列表。

    现在问题来了,由于query_article_detail函数返回的数据类型不同,如何让IDE的自动补全能够正确提示呢?例如当我们传入了一个文章id列表,但是却直接读取返回数据的.content属性,在IDE上面看不出任何问题,如下图所示。但显然会报错,因为此时的detail变量的值是一个列表。列表是没有.content属性的。

    有没有什么办法能够让IDE根据query_article_detail参数的类型,提示我们对返回数据的使用是否正确呢?

    这个场景下,就可以使用Python的typing模块中的@overload装饰器,实现函数重载来提示。示例代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    from typing import List, overload
    from dataclasses import dataclass


    @dataclass
    class ArticleDetail:
    id: int
    title: str
    content: str
    tag: List[str]


    @overload
    def query_article_detail(article_id: List[int]) -> List[ArticleDetail]:
    ...

    @overload
    def query_article_detail(article_id: int) -> ArticleDetail:
    ...


    def query_article_detail(article_id: int | List[int]) -> ArticleDetail | List[ArticleDetail]:
    if isinstance(article_id, int):
    detail = ArticleDetail(
    id=article_id,
    title='文章标题',
    content='文章内容',
    tag=['tag1', 'tag2']
    )
    return detail
    else:
    details = []
    for _id in article_id:
    detail = ArticleDetail(
    id=_id,
    title='文章标题',
    content='文章内容',
    tag=['tag1', 'tag2']
    )
    details.append(detail)
    return details

    def test_query_article_detail():
    detail = query_article_detail([123, 456, 789])
    print(detail.)

    在定义函数之前,先使用@overload装饰器,装饰两次函数名。每一次使用不同的参数:

    1
    2
    3
    4
    5
    6
    7
    @overload
    def query_article_detail(article_id: List[int]) -> List[ArticleDetail]:
    ...

    @overload
    def query_article_detail(article_id: int) -> ArticleDetail:
    ...

    这两个函数都是空函数,函数体用三个点代替。当然你也可以使用pass。而你真正的query_article_detail放到最下面。现在,当我们对detail对象使用自动补全时,IDE就能根据参数的类型来补全对应的值了。

    当传入参数是单个id时,如下图所示:

    当传入的参数是id列表时,如下图所示:

    需要注意的时,所有重载的函数与真正执行的函数,函数名必须全部相同,如下图所示:

    并且,真正实现功能的函数,必须放在重载函数的下面。

    使用这种方式,以后即时别的文件导入并使用你这个函数,你也不用担心它用错数据类型了。



沪ICP备19023445号-2号
友情链接