参数模型:位置、关键字、解包与默认值陷阱
可变数量参数:*args
使用 *args 接收任意数量的位置参数:
python
def sum_all(*numbers):
total = 0
for num in numbers:
total += num
return total
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all()) # 0*numbers 将所有位置参数收集到元组中:
python
def show_args(*args):
print(type(args)) # <class 'tuple'>
print(args)
show_args(1, 2, 3) # (1, 2, 3)混合使用
*args 必须在普通位置参数之后,关键字参数之前:
python
def func(a, b, *args, c=10):
print(f"a={a}, b={b}, args={args}, c={c}")
func(1, 2, 3, 4, 5, c=20)
# a=1, b=2, args=(3, 4, 5), c=20可变数量关键字参数:**kwargs
使用 **kwargs 接收任意数量的关键字参数:
python
def print_info(**info):
for key, value in info.items():
print(f"{key}: {value}")
print_info(name="Alice", age=30, city="NYC")
# name: Alice
# age: 30
# city: NYC**info 将所有关键字参数收集到字典中:
python
def show_kwargs(**kwargs):
print(type(kwargs)) # <class 'dict'>
print(kwargs)
show_kwargs(x=1, y=2) # {'x': 1, 'y': 2}*args 和 **kwargs 组合
python
def flexible_func(a, b, *args, c=10, **kwargs):
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"c={c}")
print(f"kwargs={kwargs}")
flexible_func(1, 2, 3, 4, c=5, x=10, y=20)
# a=1, b=2
# args=(3, 4)
# c=5
# kwargs={'x': 10, 'y': 20}参数解包:调用时的 * 和 **
解包序列:*
python
def add(a, b, c):
return a + b + c
numbers = [1, 2, 3]
print(add(*numbers)) # 6,等价于 add(1, 2, 3)
# 解包任何可迭代对象
print(add(*range(3))) # 3,等价于 add(0, 1, 2)解包字典:**
python
def greet(name, age):
print(f"{name} is {age} years old")
person = {'name': 'Alice', 'age': 30}
greet(**person) # Alice is 30 years old组合解包
python
def func(a, b, c, d, e):
return a + b + c + d + e
print(func(1, 2, *(3, 4), e=5)) # 15
print(func(*[1, 2], c=3, **{'d': 4, 'e': 5})) # 15仅限位置参数:/
Python 3.8+ 支持 / 标记,左侧参数只能通过位置传递:
python
def divide(a, b, /):
return a / b
print(divide(10, 2)) # 5.0
# print(divide(a=10, b=2)) # TypeError用途:
- 强制位置传递,避免关键字调用
- 允许未来改变参数名而不破坏 API
python
def func(pos_only, /, pos_or_kw, *, kw_only):
print(pos_only, pos_or_kw, kw_only)
func(1, 2, kw_only=3) # 正确
func(1, pos_or_kw=2, kw_only=3) # 正确
# func(pos_only=1, pos_or_kw=2, kw_only=3) # TypeError仅限关键字参数:*
* 后的参数只能通过关键字传递:
python
def greet(name, *, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", greeting="Hi") # Hi, Bob!
# greet("Carol", "Hey") # TypeError用途:
- 强制关键字传递,提高可读性
- 避免参数顺序错误
python
def create_user(username, *, email, age, city="Unknown"):
return {'username': username, 'email': email, 'age': age, 'city': city}
user = create_user("alice", email="alice@example.com", age=30)
# create_user("alice", "alice@example.com", 30) # TypeError完整参数顺序
Python 函数参数的完整顺序:
- 仅限位置参数(
/之前) - 普通参数(可位置可关键字)
*args(可变位置参数)- 仅限关键字参数(
*之后或*args之后) **kwargs(可变关键字参数)
python
def complete(pos1, pos2, /, pos_or_kw, *args, kw1, kw2=2, **kwargs):
print(f"pos1={pos1}, pos2={pos2}")
print(f"pos_or_kw={pos_or_kw}")
print(f"args={args}")
print(f"kw1={kw1}, kw2={kw2}")
print(f"kwargs={kwargs}")
complete(1, 2, 3, 4, 5, kw1=6, kw2=7, x=8, y=9)
# pos1=1, pos2=2
# pos_or_kw=3
# args=(4, 5)
# kw1=6, kw2=7
# kwargs={'x': 8, 'y': 9}默认参数陷阱详解
陷阱:可变默认值
python
def add_item(item, items=[]):
items.append(item)
return items
list1 = add_item("apple")
list2 = add_item("banana")
print(list1) # ['apple', 'banana'],不是 ['apple']!
print(list2) # ['apple', 'banana']
print(list1 is list2) # True原因:默认值 [] 在函数定义时创建一次,所有调用共享。
修复:
python
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
list1 = add_item("apple")
list2 = add_item("banana")
print(list1) # ['apple']
print(list2) # ['banana']陷阱:默认值中的时间
python
import time
def log_message(message, timestamp=time.time()):
print(f"[{timestamp}] {message}")
log_message("First")
time.sleep(2)
log_message("Second") # 时间戳相同!修复:
python
def log_message(message, timestamp=None):
if timestamp is None:
timestamp = time.time()
print(f"[{timestamp}] {message}")陷阱:默认值引用类属性
python
class Config:
DEBUG = True
def func(debug=Config.DEBUG):
print(debug)
func() # True
Config.DEBUG = False
func() # 仍然是 True(默认值在定义时绑定)实用技巧
1. 包装任意参数的装饰器
python
def trace(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@trace
def add(a, b):
return a + b
print(add(3, 5))
# Calling add with args=(3, 5), kwargs={}
# 82. 强制关键字参数提高可读性
python
def connect(*, host, port, timeout=30):
print(f"Connecting to {host}:{port} (timeout={timeout}s)")
connect(host="example.com", port=443)
# connect("example.com", 443) # TypeError,必须用关键字3. 合并字典
python
def merge_dicts(*dicts):
result = {}
for d in dicts:
result.update(d)
return result
# 或使用解包(Python 3.5+)
def merge_dicts(*dicts):
return {k: v for d in dicts for k, v in d.items()}
d1 = {'a': 1, 'b': 2}
d2 = {'b': 3, 'c': 4}
print(merge_dicts(d1, d2)) # {'a': 1, 'b': 3, 'c': 4}练习
可变参数求和:编写
multiply_all(*numbers),返回所有数字的乘积。处理空参数情况(返回 1)。字典合并:编写
merge(**dicts),接收任意数量的关键字参数,返回合并后的字典。后面的值覆盖前面的。参数转发:编写装饰器
@log_calls,打印函数调用的所有参数(位置和关键字),然后调用原函数。修复默认值陷阱:修复以下函数:
pythondef create_student(name, courses=[]): courses.append("Math") return {'name': name, 'courses': courses} alice = create_student("Alice") bob = create_student("Bob") print(alice) # 期望只有 Math print(bob) # 期望只有 Math灵活的配置函数:编写
configure(required_param, /, optional_param=10, *, flag=False, **extra),演示所有参数类型的组合使用。
完成练习后,打开本章答案页核对。
本章小结
第二章建立了函数的完整认识:
- 函数定义、参数、返回值
- LEGB 作用域规则
- 闭包捕获外层变量
- 灵活的参数模型(*args, **kwargs, /, *)
这些是 Python 函数式编程的基础。第三章将学习对象、集合与文本数据。
回到第二章概览 · 返回Python 学习路线