详解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读取sqlite数据库文件的方法分析
Aug 07 Python
Python实现曲线点抽稀算法的示例
Oct 12 Python
python微信跳一跳系列之棋子定位像素遍历
Feb 26 Python
python实现可视化动态CPU性能监控
Jun 21 Python
Python使用pickle模块储存对象操作示例
Aug 15 Python
Pandas 按索引合并数据集的方法
Nov 15 Python
python实现爬虫抓取小说功能示例【抓取金庸小说】
Aug 09 Python
python生成器推导式用法简单示例
Oct 08 Python
解决pycharm启动后总是不停的updating indices...indexing的问题
Nov 27 Python
Python参数传递对象的引用原理解析
May 22 Python
PyCharm2020.1.2社区版安装,配置及使用教程详解(Windows)
Aug 07 Python
详解Python3 定义一个跨越多行的字符串的多种方法
Sep 06 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中的strtr函数使用介绍(str_replace)
2011/10/20 PHP
php文件打包 下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件
2012/06/13 PHP
PHP与jquery实时显示网站在线人数实例详解
2016/12/02 PHP
php从数据库中获取数据用ajax传送到前台的方法
2018/08/20 PHP
PHP PDOStatement::fetch讲解
2019/01/31 PHP
PHP实现随机发扑克牌
2020/04/22 PHP
改写一个简单的菜单 弹性大小
2010/12/02 Javascript
让textarea自动调整大小的js代码
2011/04/12 Javascript
使用javascript实现有效时间的控制,并显示将要过期的时间
2014/01/02 Javascript
js showModalDialog弹出窗口实例详解
2014/01/07 Javascript
Javascript中构造函数要注意的一些坑
2017/01/23 Javascript
js实现返回顶部效果
2017/03/10 Javascript
JS实现多级菜单中当前菜单不随页面跳转样式而发生变化
2017/05/30 Javascript
一个简易的js图片轮播效果
2017/07/22 Javascript
vue动态路由实现多级嵌套面包屑的思路与方法
2017/08/16 Javascript
vue 自定义组件 v-model双向绑定、 父子组件同步通信的多种写法
2017/11/27 Javascript
layui点击按钮添加可编辑的一行方法
2018/08/15 Javascript
浅谈vue引用静态资源需要注意的事项
2018/09/28 Javascript
JavaScript设计模式之代理模式实例分析
2019/01/16 Javascript
Vue 实现前端权限控制的示例代码
2019/07/09 Javascript
python使用append合并两个数组的方法
2015/04/28 Python
Django项目中包含多个应用时对url的配置方法
2018/05/30 Python
django.db.utils.ProgrammingError: (1146, u“Table‘’ doesn’t exist”)问题的解决
2018/07/13 Python
Django之提交表单与前后端交互的方法
2019/07/19 Python
python输出数组中指定元素的所有索引示例
2019/12/06 Python
Html5移动端适配IphoneX等机型的方法
2019/06/25 HTML / CSS
原装进口全世界:天猫国际
2016/08/03 全球购物
中国第一家杂志折扣订阅网:杂志铺
2016/08/30 全球购物
澳大利亚首屈一指的在线购物目的地:Kogan.com
2017/02/02 全球购物
SISLEY希思黎官方旗舰店:享誉全球的奢华植物美容品牌
2018/04/25 全球购物
Farfetch台湾官网:奢侈品牌时尚购物平台
2019/06/17 全球购物
System.Array.CopyTo()和System.Array.Clone()有什么区别
2016/06/20 面试题
会计系毕业生求职信
2014/05/28 职场文书
高中班主任工作总结(范文)
2019/08/20 职场文书
Python数据分析之pandas函数详解
2021/04/21 Python
ubuntu如何搭建vsftpd服务器
2022/12/24 Servers