解包
解包的价值不在于省字符,在于不用写下标。下标是硬编码的位置,写错一个数字, 代码照样能跑,只是结果悄悄错了。解包把「第 0 个是什么、第 1 个是什么」写成了名字。
而且解包不只对元组有效,对任何可迭代对象都有效 —— 包括不支持 [] 的迭代器。
>>> lax_coordinates = (33.9425, -118.408056)>>> latitude, longitude = lax_coordinates交换两个变量不用临时变量:
>>> b, a = a, b其实右边先造了一个元组 (a, b),再解包回去。所以这不是什么语法糖特例,就是解包。
函数调用时加 * 把序列摊开:
>>> t = (20, 8)>>> divmod(*t)(2, 4)>>> quotient, remainder = divmod(*t)函数返回多个值就是靠这个:返回元组,调用方解包。
>>> import os>>> _, filename = os.path.split('/home/luciano/.ssh/id_rsa.pub')>>> filename'id_rsa.pub'os.path.split() 返回 (head, tail) 两个值。只要文件名,就用 _ 丢掉头。
用 * 收多余的元素
Section titled “用 * 收多余的元素”*args 收任意多个位置参数是老特性。Python 3 把它扩展到了赋值语句:
>>> a, b, *rest = range(5)>>> a, b, rest(0, 1, [2, 3, 4])
>>> a, b, *rest = range(2) # 一个都不剩也行>>> a, b, rest(0, 1, [])三个必须记住的规则:
- 一个赋值里只能有一个
*。 *可以在任何位置,不用非得在末尾。rest永远是 list,哪怕只有一个元素,哪怕是空的。
>>> a, *body, c, d = range(5)>>> a, body, c, d(0, [1, 2], 3, 4)
>>> *head, b, c, d = range(5)>>> head, b, c, d([0, 1], 2, 3, 4)* 还能用在函数调用和字面量里(PEP 448)
Section titled “* 还能用在函数调用和字面量里(PEP 448)”函数调用里可以出现多个 *:
>>> def fun(a, b, c, d, *rest):... return a, b, c, d, rest>>> fun(*[1, 2], 3, *range(4, 7))(1, 2, 3, 4, (5, 6))构造 list / tuple / set 字面量时也能用:
>>> *range(4), 4(0, 1, 2, 3, 4)>>> [*range(4), 4][0, 1, 2, 3, 4]>>> {*range(4), 4, *(5, 6, 7)}{0, 1, 2, 3, 4, 5, 6, 7}解包的目标可以带嵌套,只要值的结构对得上:
for name, _, _, (lat, lon) in metro_areas: if lon <= 0: print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')最后那个字段 (35.689722, 139.691667) 直接在 for 里被拆成 lat 和 lon,
一行都不用多写。
一个冷门的用法:目标是列表
Section titled “一个冷门的用法:目标是列表”大多数时候解包到元组就够了。主要有用的场景是「确认查询结果只有一条」:
>>> [record] = query_returning_single_row()这不是解包语法,是列表解包:如果查询返回了两条,会抛 ValueError。
比先取出来再检查长度更省事。
只有一个字段的话:
>>> [[field]] = query_returning_single_row_with_single_field()tests/test_ch02.py 里 test_nested_unpacking_and_match_agree 断言了嵌套解包
和 match/case 两种写法得到同样的结果。