位置参数与关键字参数
调用函数时,Python 提供了两种传参方式:位置参数按顺序匹配,关键字参数按名称匹配。两种方式可以混用,但必须遵守严格的顺序规则。
位置传参
位置参数是最直观的传参方式:实参按出现顺序依次绑定到形参:
def describe_pet(animal, name, age):
return f"{name} 是一只 {age} 岁的 {animal}"
print(describe_pet("狗", "旺财", 3))
# 旺财 是一只 3 岁的 狗
位置传参要求调用者记住参数顺序。顺序错误会导致逻辑错误,且不会报错:
# 顺序写反了,但语法完全合法
print(describe_pet(3, "狗", "旺财"))
# 狗 是一只 旺财 岁的 3 —— 语义荒谬
关键字传参
关键字参数通过 name=value 的形式显式指定形参,不依赖顺序:
print(describe_pet(animal="狗", name="旺财", age=3))
print(describe_pet(age=3, animal="狗", name="旺财")) # 顺序任意
关键字参数让代码自文档化,调用者无需查看函数定义就能理解每个值的含义。对于参数较多的函数,强烈建议使用关键字参数。
混合使用与顺序规则
位置参数和关键字参数可以混用,但位置参数必须排在关键字参数之前。这是 Python 的硬性语法规则:
# 合法:位置在前,关键字在后
describe_pet("狗", name="旺财", age=3)
describe_pet("狗", "旺财", age=3)
# 非法:关键字参数后接位置参数
# describe_pet(animal="狗", "旺财", 3)
# SyntaxError: positional argument follows keyword argument
这个规则的存在是因为解析器需要明确每个实参对应哪个形参。一旦开始用关键字传参,后续所有参数都必须用关键字,否则解析器无法判断位置实参应该跳过哪些已赋值的形参。
重复赋值错误
不能对同一个形参同时用位置和关键字传参:
def set_point(x, y):
return (x, y)
# set_point(1, x=2)
# TypeError: set_point() got multiple values for argument 'x'
这里 1 按位置绑定给 x,然后 x=2 试图再次给 x 赋值,Python 拒绝这种歧义。
关键字参数与可读性
当函数有多个布尔标志或数值参数时,关键字参数能极大提升可读性:
def send_email(to, subject, body, cc=None, bcc=None, urgent=False):
pass
# 不用关键字,很难看懂第三个 False 是什么意思
# send_email("boss@example.com", "周报", "请查收", None, None, False)
# 用关键字,一目了然
send_email(
to="boss@example.com",
subject="周报",
body="请查收",
urgent=False
)
强制关键字参数的技巧
在定义函数时,可以在参数列表中放一个孤立的 *(不是 *args),其后的所有参数都必须用关键字传递:
def safe_divide(a, b, *, precision=2):
result = a / b
return round(result, precision)
print(safe_divide(10, 3, precision=4)) # 合法
# safe_divide(10, 3, 4) # 非法!precision 必须关键字传参
# TypeError: safe_divide() takes 2 positional arguments but 3 were given
这种写法在 API 设计中非常有用:它防止调用者因多传一个位置参数而意外改变语义。
位置参数与可变参数的交互
当函数定义包含 *args 时,*args 之前的参数可以按位置或关键字传递(除非有 / 限制),*args 收集多余的位置参数。*args 之后的参数则必须用关键字:
def log_message(level, *args, timestamp=None):
msg = " ".join(str(a) for a in args)
if timestamp:
return f"[{timestamp}] {level}: {msg}"
return f"{level}: {msg}"
print(log_message("INFO", "用户", "登录", timestamp="2024-01-01"))
# [2024-01-01] INFO: 用户 登录
常见错误
关键字参数放错位置:
def demo(a, b, c):
pass
# demo(a=1, 2, 3) # SyntaxError
# demo(1, b=2, 3) # SyntaxError
拼错关键字参数名:
# demo(a=1, bb=2, c=3) # TypeError: demo() got an unexpected keyword argument 'bb'
遗漏必需参数:
# demo(1) # TypeError: demo() missing 1 required positional argument: 'c'
# demo(a=1, c=3) # TypeError: demo() missing 1 required positional argument: 'b'
小结
位置参数依赖顺序,关键字参数依赖名称。混用时位置参数必须在前。关键字参数提升可读性,配合 * 可强制要求关键字传参。掌握这两种方式及其规则,是写出清晰、健壮函数调用的基础。