(二)把实参安排到参数位置
1. 位置参数、关键字参数与默认值
让“达标分钟数”也能由调用者决定:
# goal 未传入时使用默认值 30;传入时使用这次指定的值。
def count_met(values, goal=30):
count = 0
for minutes in values:
if minutes >= goal:
count += 1
return count
print(count_met([20, 35, 45]))
print(count_met([20, 35, 45], 40))
# 写出参数名后,按名称绑定,不依靠它们在调用中的先后位置。
print(count_met(goal=40, values=[20, 35, 45]))输出 2、1、1。不写名称时,实参从左到右对应形参;写成 goal=40 时,明确把 40 交给 goal。通常先写位置实参,再写关键字实参。
调用 count_met([20], 30, goal=40),同一个 goal 同时收到两次值,Python 会报告 TypeError。错误发生在参数绑定阶段,函数体还没有开始累加。把调用改成 count_met([20], goal=40),每个参数便各有一份来源。
2. 用 *args 与 **kwargs 收集实参
有时希望把多天的时长直接写在括号里,而不先建立列表:
# 定义中的 *minutes 把多余的位置实参收集成元组。
def add_minutes(*minutes):
print(minutes)
return sum(minutes)
print(add_minutes(20, 35, 45))
# 定义中的 **fields 把未被其他形参接收的关键字实参收集成字典。
def describe(title, **fields):
return title, fields
print(describe("Python", author="小林", finished=False))输出中的 minutes 是 (20, 35, 45),合计为 100;最后得到 ('Python', {'author': '小林', 'finished': False})。args、kwargs 是常见名称,决定行为的是前面的星号,名称可以换成能说明数据含义的词。
调用处也能写星号,但方向相反:定义处收集,调用处拆开。
# 调用中的 * 把序列拆成位置实参,** 把映射拆成关键字实参。
def describe(title, *tags, finished=False, **fields):
return title, tags, finished, fields
tags = ["入门", "语法"]
options = {"finished": True, "author": "小林"}
print(describe("Python", *tags, **options))结果是 ('Python', ('入门', '语法'), True, {'author': '小林'})。finished 被同名形参接收,剩下的 author 才进入 fields。用 ** 传关键字时,映射的键是参数名字符串;若同时写出 finished=False,又从 options 传入 finished=True,同样会发生重复绑定。
3. / 与 * 指定调用方式
多个布尔开关挤在一起时,make_report(data, True, False) 很难看出各个值在控制什么。可以把开关规定为按名称传入:
# / 前面的 values 按位置传;裸 * 后面的 goal、label 按名称传。
def make_report(values, /, *, goal=30, label="本周"):
completed = sum(minutes >= goal for minutes in values)
return label, completed
print(make_report([20, 35, 45], goal=40, label="周末"))得到 ('周末', 1)。/ 和裸 * 是参数列表中的分隔符,不会接收一个实际值。这里布尔结果参与求和,True 计为 1,False 计为 0。
| 定义中的位置 | 调用时的写法 | 例子 |
|---|---|---|
/ 之前 |
位置实参 | make_report(data, ...) |
/ 与 * 之间 |
位置或关键字实参 | count_met 中的普通参数 goal |
裸 * 或 *args 之后 |
关键字实参 | goal=40、finished=True |
*args、**kwargs |
分别收集位置实参和关键字实参 | tags 元组、fields 字典 |
读到 def f(a, /, b=0, *args, c=1, **kwargs) 时,可以依次找“只按位置”“普通参数”“剩余位置”“按名称”“剩余关键字”这几段。自己写小函数时,先用能表达任务的简单参数;出现具体的调用歧义后,再增加分隔符。函数定义与参数