STAR UNPACKING / 04

星号装配线

同一个星号站在不同位置,动作正好相反:定义参数时把多出来的值收拢;调用函数时把容器里的值拆开。选几组零件,看看接收端最终拿到什么。

定义时收
调用时拆
def publish(title, *sections, format="HTML", **options):

接收端拆箱

先展开调用零件,再按函数签名绑定。

sections
总是一个 tuple
没有额外位置值:()
options
每次调用新建 dict
没有额外关键字:{}

别把两侧的星号读反

读代码时先问一句:“这是函数定义,还是函数调用?”位置一确定,星号方向就清楚了。接收方的变量名并不强制叫 args 或 kwargs,那只是最常见的约定。

站在参数列表:收集

def log(*items, **fields):
    print(items)   # 元组
    print(fields)  # 字典
多出来的实参 → 两个容器*items 收额外位置实参;**fields 收没有匹配到显式参数的关键字实参。

站在调用表达式:展开

items = ["警告", "磁盘"]
fields = {"level": 2}
log(*items, **fields)
两个容器 → 一串实参* 的对象必须可迭代;** 的对象必须是映射,并且交给函数调用的键必须是字符串。
展开遵守当前位置列表元素按迭代顺序变成位置实参;映射条目按键名变成关键字实参。
可以展开不止一次一次调用里可以出现多组 ***,阅读时按从左到右组合。
调用不能有重复关键字显式写了 format=...,又从 ** 展开出 format,会在调用阶段触发 TypeError,不会采用“后者覆盖前者”。