详解Python 函数参数的拆解


Posted in Python onSeptember 02, 2020

本文为阅读 《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 函数参数拆解的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
在Python中使用sort()方法进行排序的简单教程
May 21 Python
Python设计模式之原型模式实例详解
Jan 18 Python
Python基于opencv调用摄像头获取个人图片的实现方法
Feb 21 Python
python判断所输入的任意一个正整数是否为素数的两种方法
Jun 27 Python
Python(PyS60)实现简单语音整点报时
Nov 18 Python
Python 内置变量和函数的查看及说明介绍
Dec 25 Python
多个python文件调用logging模块报错误
Feb 12 Python
python如何把字符串类型list转换成list
Feb 18 Python
解决windows上安装tensorflow时报错,“DLL load failed: 找不到指定的模块”的问题
May 20 Python
python 实现关联规则算法Apriori的示例
Sep 30 Python
Python 使用office365邮箱的示例
Oct 29 Python
宝塔更新Python及Flask项目的部署
Apr 11 Python
Python 常用日期处理 -- calendar 与 dateutil 模块的使用
Sep 02 #Python
python 常用日期处理-- datetime 模块的使用
Sep 02 #Python
详解Python中的路径问题
Sep 02 #Python
python dict如何定义
Sep 02 #Python
python基本算法之实现归并排序(Merge sort)
Sep 01 #Python
在pycharm中文件取消用 pytest模式打开的操作
Sep 01 #Python
Python内置函数property()如何使用
Sep 01 #Python
You might like
PHPWind 发帖回帖Api PHP版打包下载
2010/02/08 PHP
PHP获取MAC地址的函数代码
2011/09/11 PHP
利用PHP扩展vld查看PHP opcode操作步骤
2013/03/04 PHP
php网站判断用户是否是手机访问的方法
2013/11/01 PHP
phpmyadmin提示The mbstring extension is missing的解决方法
2014/12/17 PHP
php中get_cfg_var()和ini_get()的用法及区别
2015/03/04 PHP
YII中Ueditor富文本编辑器文件和图片上传的配置图文教程
2017/03/15 PHP
JQuery slideshow的一个小问题(如何发现及解决过程)
2013/02/06 Javascript
深入理解javascript原型链和继承
2014/09/23 Javascript
JS模拟并美化的表单控件完整实例
2015/08/19 Javascript
JavaScript简单下拉菜单实例代码
2015/09/07 Javascript
基于replaceChild制作简单的吞噬特效
2015/09/21 Javascript
js数组如何添加json数据及js数组与json的区别
2015/10/27 Javascript
Immutable 在 JavaScript 中的应用
2016/05/02 Javascript
jQuery弹出窗口简单实现代码
2017/03/09 Javascript
JS 插件dropload下拉刷新、上拉加载使用小结
2017/04/13 Javascript
bootstrap+jQuery 实现下拉菜单中复选框全选和全不选效果
2017/06/12 jQuery
使用JavaScript实现在页面中显示距离2017年中秋节的天数
2017/09/26 Javascript
vuejs实现标签选项卡动态更改css样式的方法
2018/05/31 Javascript
Nodejs核心模块之net和http的使用详解
2019/04/02 NodeJs
在Vue中使用HOC模式的实现
2020/08/23 Javascript
python和shell变量互相传递的几种方法
2013/11/20 Python
Python中使用第三方库xlutils来追加写入Excel文件示例
2015/04/05 Python
python3实现磁盘空间监控
2018/06/21 Python
在python中使用with打开多个文件的方法
2019/01/07 Python
一文秒懂python读写csv xml json文件各种骚操作
2019/07/04 Python
Python 将json序列化后的字符串转换成字典(推荐)
2020/01/06 Python
css3实例教程 一款纯css3实现的发光屏幕旋转特效
2014/12/07 HTML / CSS
HTML5声音录制/播放功能的实现代码
2018/05/03 HTML / CSS
办理护照介绍信
2014/01/16 职场文书
新年寄语大全
2014/04/12 职场文书
酒店优秀员工事迹材料
2014/06/02 职场文书
不遵守课堂纪律的检讨书
2014/09/24 职场文书
党员群众路线教育实践活动剖析材料
2014/10/10 职场文书
postgresql使用filter进行多维度聚合的解决方法
2021/07/16 PostgreSQL
TV动画《间谍过家家》公开PV
2022/03/20 日漫