详解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中使用pyhook实现键盘监控的例子
Jul 18 Python
Python脚本判断 Linux 是否运行在虚拟机上
Apr 25 Python
基于python的Tkinter实现一个简易计算器
Dec 31 Python
python中urlparse模块介绍与使用示例
Nov 19 Python
Pandas之drop_duplicates:去除重复项方法
Apr 18 Python
Flask之请求钩子的实现
Dec 23 Python
Python字典深浅拷贝与循环方式方法详解
Feb 09 Python
Idea安装python显示无SDK问题解决方案
Aug 12 Python
Python reversed反转序列并生成可迭代对象
Oct 22 Python
Python基于Tkinter开发一个爬取B站直播弹幕的工具
May 06 Python
Python词云的正确实现方法实例
May 08 Python
Django中session进行权限管理的使用
Jul 09 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
提升PHP执行速度全攻略(下)
2006/10/09 PHP
PHP 和 MySQL 基础教程(四)
2006/10/09 PHP
PHP轻量级数据库操作类Medoo增加、删除、修改、查询例子
2014/07/04 PHP
PHP自带ZIP压缩、解压缩类ZipArchiv使用指南
2015/03/03 PHP
PHP基于XMLWriter操作xml的方法分析
2017/07/17 PHP
PHP使用PDO 连接与连接管理操作实例分析
2020/04/21 PHP
锋利的jQuery 要点归纳(一) jQuery选择器
2010/03/21 Javascript
jQuery 在光标定位的地方插入文字的插件
2012/05/10 Javascript
JQuery中属性过滤选择器用法实例分析
2015/05/18 Javascript
JavaScript使表单中的内容显示在屏幕上的方法
2015/06/29 Javascript
JavaScript给每一个li节点绑定点击事件的实现方法
2016/12/01 Javascript
用Vue.extend构建消息提示组件的方法实例
2017/08/08 Javascript
jQuery 实现倒计时天,时,分,秒功能
2018/07/31 jQuery
Vue.js获取被选择的option的value和text值方法
2018/08/24 Javascript
微信小程序模板template简单用法示例
2018/12/04 Javascript
js实现鼠标拖拽缩放div实例代码
2019/03/25 Javascript
跟老齐学Python之折腾一下目录
2014/10/24 Python
使用Python读写及压缩和解压缩文件的示例
2016/07/08 Python
Python爬取APP下载链接的实现方法
2016/09/30 Python
python进程管理工具supervisor的安装与使用教程
2017/09/05 Python
python自制包并用pip免提交到pypi仅安装到本机【推荐】
2019/06/03 Python
python3.7简单的爬虫实例详解
2019/07/08 Python
Python模拟登入的N种方式(建议收藏)
2020/05/31 Python
python matplotlib绘制三维图的示例
2020/09/24 Python
美国大尺码女装零售商:TORRID
2016/10/01 全球购物
上海奥佳笔试题面试题
2016/11/16 面试题
客运企业隐患排查工作方案
2014/06/06 职场文书
六查六看个人剖析材料
2014/10/14 职场文书
单位实习工作证明怎么写
2014/11/02 职场文书
小学校长个人总结
2015/03/03 职场文书
自主招生自荐信怎么写
2015/03/24 职场文书
2016年幼儿园教研活动总结
2016/04/05 职场文书
springboot如何接收application/x-www-form-urlencoded类型的请求
2021/11/02 Java/Android
CSS3实现指纹特效代码
2022/03/17 HTML / CSS
【海涛解说】史上最给力比赛,挑战DOTA极限
2022/04/01 DOTA
Win10加载疑难解答时出错发生意外错误的解决方法
2022/07/07 数码科技