本文为阅读 《Python Tricks: The Book》一书的 3.5 Function Argument Unpacking 的笔记与扩充理解。函数参数拆解是定义可变参数(VarArgs) *args 和 **kwargs 的反向特性。

*args 和 **kwars 是函数可定义一个形参来接收传入的不定数量的实参。

而这里的函数参数拆解是形参定义多个,在调用时只传入一个集合类型对象(带上 * 或 ** 前缀),如 list, tuple, dict, 甚至是 generator, 然后函数能自动从集合对象中取得对应的值。

如果能理解下面赋值时的参数拆解和 Python 3.5 的新增 * ** 操作,那么于本文讲述的特性就好理解了。

唯一的不同时作为参数的集合传入函数时必须前面加上* 或 **, 以此宣告该参数将被拆解,而非一个整体作为一个函数参数。加上* 或**与 Java 的 @SafeVarargs有类似的功效,最接近的是 Scala 的 foo(Array[String]("d", "e") : _*)写法。参见:Java 和 Scala 调用变参的方式

Python 的赋值拆解操作

Python 3.5 的新增拆解操作

有些像是函数编程中的flatten或unwrap操作。

有了上面的基础后,再回到原书中的例子,当我们定义如下打印 3-D 坐标的函数

依次传入三个参数的方式就不值不提了,现在就看如何利用函数的参数拆解特性,只传入一个集合参数,让该 print_vector函数准确从集合中获得相应的x, y, 和 z的值。

函数参数拆解的调用举例

注意**dict_vec有点不一样,它的内容必须是函数 print_vector的形参 'x', 'y', 'z' 作为 key 的三个元素。

以下是各种错误

**dict_vec 元素个数不对,或 key 不匹配时的错误

不带星星的错误

把集合对象整体作为第一个参数,所以未传入 y 和 z,因此必须用前缀 * 或 ** 通告函数进行参数拆解

集合长度与函数参数个数不匹配时的错误

上面这两个错误与赋值时的拆解因元素个数不匹配时的错误是相对应的

当然在赋值时 Python 可以像下面那样做

补充(2020-07-02): 迭代的拆解在 Python 中的术语是 Iterable Unpacking, 找到两个相关的 PEP 448, PEP 3132。在实际上用处还是很大的,比如在拆分字符串时只关系自己有兴趣的字段

这样就避免了用索引号来引用拆分后的值,如 split[0], splint[2] 等,有名的变量不容易出错。注意到 Python 在拆解时非常聪明,它知道怎么去对应位置,用了星号(*) 的情况,明白如何处理前面跳过多少个,中间跳过多少个,或最后收集多少个元素。