详解Python 函数参数的拆解

本文为阅读 《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 的赋值拆解操作

>>> a, b = [1, 2]  # a, b = (1, 2) 也是一样的效果
>>> print(a, b)
1 2
>>> a, b = {'x': 1, 'y':2}
>>> print(a, b)
x y
>>> a, b = {'x': 1, 'y':2}.keys()
>>> print(a, b)
x y
>>> a, b = {'x': 1, 'y':2}.values()
>>> print(a, b)
1 2
>>> a, b = (x * x for x in range(2))
>>> print(a, b)
0 1

Python 3.5 的新增拆解操作

>>> [1, 2, *range(3), *[4, 5], *(6, 7)]  # * 号能把集合打散,flatten(unwrap)
[1, 2, 0, 1, 2, 4, 5, 6, 7]
>>> {'x': 1, **{'y': 2, 'z': 3}}      # ** 把字典打散, flatten(unwrap) 操作
{'x': 1, 'y': 2, 'z': 3}

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

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

def print_vector(x, y, z):
  print('<%s, %s, %s>' % (x, y, z))

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

函数参数拆解的调用举例

>>> list_vec = [2, 1, 3]
>>> print_vector(*list_vec)
<2, 1, 3>
>>> print_vector(*(2, 1, 3))
<2, 1, 3>
>>> dict_vec = {'y': 2, 'z': 1, 'x': 3}
>>> print_vector(*dict_vec)  # 相当于 print_vector(*dict_vec.keys())
<y, z, x>
>>> print_vector(**dict_vec)  # 相当于 print_vector(dict_vec['x'], dict_vec['y'], dict_vec['z']
<3, 2, 1>
>>> genexpr = (x * x for x in range(3))
>>> print_vector(*genexpr)
<0, 1, 4>
>>> print_vector(*dict_vec.values()) # 即 print_vector(*list(dict_vec.values()))
<2, 1, 3>

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

以下是各种错误

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

>>> print_vector(**{'y': 2, 'z': 1, 'x': 3})
<3, 2, 1>
>>> print_vector(**{'y': 2, 'z': 1, 'a': 3})    #元素个数是3 个,但出现 x, y, z 之外的 key
Traceback (most recent call last):
 File "<pyshell#39>", line 1, in <module>
  print_vector(**{'y': 2, 'z': 1, 'a': 3})
TypeError: print_vector() got an unexpected keyword argument 'a'
>>> print_vector(**{'y': 2, 'z': 1, 'x': 3, 'a': 4}) # 包含有 x, y, z, 但有四个元素,key 'a' 不能识别
Traceback (most recent call last):
 File "<pyshell#40>", line 1, in <module>
  print_vector(**{'y': 2, 'z': 1, 'x': 3, 'a': 4})
TypeError: print_vector() got an unexpected keyword argument 'a'
>>> print_vector(**{'y': 2, 'z': 1})     # 缺少 key 'x' 对应的元素
Traceback (most recent call last):
 File "<pyshell#41>", line 1, in <module>
  print_vector(**{'y': 2, 'z': 1})
TypeError: print_vector() missing 1 required positional argument: 'x'

不带星星的错误

>>> print_vector([2, 1, 3])
Traceback (most recent call last):
 File "<pyshell#44>", line 1, in <module>
  print_vector([2, 1, 3])
TypeError: print_vector() missing 2 required positional arguments: 'y' and 'z'

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

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

>>> print_vector(*[2, 1])        # 拆成了 x=2, y=1, 然后 z 呢?
Traceback (most recent call last):
 File "<pyshell#47>", line 1, in <module>
  print_vector(*[2, 1])
TypeError: print_vector() missing 1 required positional argument: 'z'
>>> print_vector(*[2, 1, 3, 4])    # 虽然拆出了 x=2, y=1, z=3, 但也别想强塞第四个元素给该函数(只定义的三个参数)
Traceback (most recent call last):
 File "<pyshell#48>", line 1, in <module>
  print_vector(*[2, 1, 3, 4])
TypeError: print_vector() takes 3 positional arguments but 4 were given

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

>>> a, b = [1]
Traceback (most recent call last):
 File "<pyshell#54>", line 1, in <module>
  a, b = [1]
ValueError: not enough values to unpack (expected 2, got 1)
>>> a, b = [1, 2, 3]
Traceback (most recent call last):
 File "<pyshell#55>", line 1, in <module>
  a, b = [1, 2, 3]
ValueError: too many values to unpack (expected 2)

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

a, b, *c = [1, 2, 3, 4]
>>> print(a, b, c)
1 2 [3, 4]

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

line = '2020-06-19 22:14:00    2688 abc.json'
date, time, size, name = line.split()  # 获得所有字段值
_, time, _, name = line.split()     # 只对 time 和 name 有兴趣
date, *_ = line.split()         # 只对第一个 date 有兴趣
*_, name = line.split()         # 只对最后的 name 有兴趣
date, *_, name = line.split()      # 对两边的 date, name 有兴趣

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

链接:

PEP 448 -- Additional Unpacking Generalizations
PEP 3132 -- Extended Iterable Unpacking

以上就是详解Python 函数参数的拆解的详细内容,更多关于python 函数参数拆解的资料请关注我们其它相关文章!

(0)

相关推荐

  • python-sys.stdout作为默认函数参数的实现

    假设我们有以下虚拟函数: import sys def writeline(text, stream=sys.stdout): stream.write(text + ' ') with open('/path/to/file', 'w') as f: # writes to /path/to/file writeline('foo', f) # writes to standard output writeline('bar') 鉴于Python在定义时评估了函数的默认参数,将sys.stdo

  • Python OpenCV 使用滑动条来调整函数参数的方法

    引言 在观察OpenCV中某个函数在不同参数的情况下,所得到的效果的时候,我之前是改一次参数运行一次,这样做起来操作麻烦,效率低下.为了更便捷的观察参数变化时带来的处理效果改变 可以使用滑动条来改变参数 具体思路 使用cv2.createTrackbar()创建滑动条,有几个参数就创建几个 对每个参数定义回调函数 在回调函数中显示图片 注意 滑动条的窗口名称 要与 图片显示的窗口名字相同 代码实现 import cv2 d = 0 color = 0 space = 0 def change_d

  • Python函数参数定义及传递方式解析

    python中函数定义参数有四种形式: def fun1(a,b,c): pass def fun2(a=1,b=2,c=3): pass def fun3(*args): pass def fun4(**kargs): pass 四种中最常见是前两种,基本上一般点的教程都会涉及,后两种一般很少单独出现,常用在混合模式中 第一种 fun1(a,b,c)是直接将实参赋予形参,根据位置做匹配,即严格要求实参的数量与行参的数量位置相等,比较一般,大多数语言常用这种方式. 第二种 fun2(a=1,b=

  • Python函数参数操作详解

    本文实例讲述了Python函数参数操作.分享给大家供大家参考,具体如下: 简述 在 Python 中,函数的定义非常简单,满足对应的语法格式要求即可.对于调用者来说,只需关注如何传递正确的参数,以及获取相应的返回值就足够了,无需了解函数的内部实现(除非想学习.跟踪源码). 话虽如此,但对于函数的定义来说,灵活性非常高.除了常规定义的必选参数以外,还支持默认参数.可变参数.以及关键字参数.这样以来,不但能处理复杂的参数,还可以简化调用者的代码. 形参和实参 不止 Python,几乎所有的编程语言都

  • python函数参数(必须参数、可变参数、关键字参数)

    #python中的函数定义,使用和传参 ###------------------- 必要参数 ------------------------------------- 必要参数,即函数调用时必须要传的参数, #下面进行举例说明 def helloWorld(a): print('输出:hello') if __name__ == '__main__': helloWorld("aaa") ## 必须要有参数 ##------------ 默认参数 -----------------

  • python+opencv边缘提取与各函数参数解析

    前情提要:作为刚入门机器视觉的小伙伴,第一节课学到机器视觉语法时觉得很难理解, 很多人家的经验,我发现都千篇一律,功能函数没解析,参数不讲解,就一个代码,所以在此将搜集的解析和案例拿出来汇总!!! 一.opencv+python环境搭建 其实能写python的就能写opencv,但是工具很总要,代码提示也很重要,你可能会用submit     vs等工具,submit编码个人觉得不够智能,vs的话过完年我学的方向不一致,所以没用 推荐 pycharm ,在项目setting中的项目解释器中安装

  • Python函数参数分类原理详解

    一.参数的定义 1.函数的参数在哪里定义 在python中定义函数的时候,函数名后面的括号里就是用来定义参数的,如果有多个参数的话,那么参数之间直接用逗号,隔开 案列: # 利用函数的参数,定义一个可以完成任意两个数相加的函数 def add_num(a,b): c = a + b print(c) 2.带参数的函数调用: 函数定义了参数,那么调用函数的时候就需要传入参数 add_num(11,22) #运行结果 33 上面的案列中,我们定义函数的时候在函数名后面的括号里定义的参数叫做形参, 而

  • Python 限定函数参数的类型及默认值方式

    python作为一门动态语言,在使用变量之前是不需要进行定义,而是通过动态绑定的方法将变量绑定为某种类型.这样做为我们使用变量时提供了方便,但有时也给我们使用变量时造成了一定的困扰,例如在使用变量时不知道变量的类型,使用函数时不知道该传什么类型·的参数.有时候我们定义函数时也想限定参数类型,本片博文主要就是为了解决定义函数时,如何限定参数的类型. 其实在python中限定函数的类型是很简单的,例如: def foo(text:'str',max:'int > 0' = 100,min:'int

  • Python中scatter函数参数及用法详解

    最近开始学习Python编程,遇到scatter函数,感觉里面的参数不知道什么意思于是查资料,最后总结如下: 1.scatter函数原型 2.其中散点的形状参数marker如下: 3.其中颜色参数c如下: 4.基本的使用方法如下: #导入必要的模块 import numpy as np import matplotlib.pyplot as plt #产生测试数据 x = np.arange(1,10) y = x fig = plt.figure() ax1 = fig.add_subplot

  • python3中函数参数的四种简单用法

    下面给大家介绍python3中函数参数的四种简单用法,具体内容如下所示: def print_two(*args): arg1, arg2 = args print "arg1: %r, arg2: %r" % (arg1,arg2) def print_two_again(arg1, arg2): print "arg1: %r, arg2: %r" % (arg1, arg2) def print_one(arg1): print "arg1: %r&

随机推荐