Python 3.8+ 可变位置解包参数的正确typing类型标注方法
Python 3.8+ 可变位置参数的类型标注正确写法
问题背景
现有一个接收任意数量同类型参数的函数,初始未加类型标注的代码如下:
def fun(*foos): for foo in foos: foo.do_something()
已知所有传入的可变位置参数类型均为foo_type,需要为foos参数补充合规的类型标注。目前存在两种写法:
- 第一种是直觉式写法,引入
typing.Tuple做标注:
from typing import Tuple def fun(*foos: Tuple[foo_type]) -> None: for foo in foos: foo.do_something()
- 第二种是CLion IDE给出的推荐写法:
def fun(*foos: foo_type) -> None: for foo in foos: foo.do_something()
结论
第二种IDE推荐的写法是完全符合PEP 484类型规范的正确写法,第一种写法是错误的。
原因说明
- 按照Python类型标注的官方规则,对于
*foos这类可变位置参数,冒号后面写的类型,指代的是每一个单独传入的位置参数的类型,而不是所有参数被打包后形成的foos元组整体的类型。
当你写*foos: foo_type时,类型检查器会自动推导函数内部的foos变量类型为tuple[foo_type, ...](即元素类型全为foo_type的可变长度元组),和实际运行时的结构完全匹配:你可以传入任意个foo_type类型的参数,循环取出的每个foo也会被识别为foo_type类型,调用do_something()不会报类型错误。 - 第一种写法的问题在于,它相当于告诉类型检查器,每个传入的可变位置参数都必须是
Tuple[foo_type]类型(也就是只包含一个foo_type元素的元组),和实际期望传入foo_type类型参数的需求完全相悖。此时循环拿到的每个foo会被识别为单元素元组类型,调用do_something()方法会直接触发类型错误,完全不符合预期。
补充:如果要手动标注函数内部
foos元组的类型(比如写返回值类型、做变量类型断言的时候),才需要用到tuple[foo_type, ...](Python 3.9+)或者Tuple[foo_type, ...](Python 3.8及更早版本)的写法,但这个类型是给函数内部的foos元组变量用的,不能写在*foos后面的参数标注位置。
内容的提问来源于stack exchange,提问作者eugenkaltenegger
相关产品推荐
相关产品推荐

