第35集:函数参数——可变参数
学习目标
- 理解可变参数的概念
- 掌握*args的使用方法
- 掌握**kwargs的使用方法
- 学会混合使用各种参数类型
一、什么是可变参数?
可变参数是指可以接受任意数量参数的函数参数。
两种可变参数类型
| 类型 | 语法 | 用途 |
|---|---|---|
| 位置可变参数 | *args |
接收任意数量的位置参数 |
| 关键字可变参数 | **kwargs |
接收任意数量的关键字参数 |
生活类比
- 可变参数就像"无限容量的购物袋"
- 可以装任意数量的物品
- 不用提前知道具体数量
二、*args:位置可变参数
基本语法
def 函数名(*args):
函数体特点
*args在函数内部是一个元组- 可以接收任意数量的位置参数
- 参数名可以是任意名称(通常用args)
示例1:计算任意数的和
def add_all(*args):
"""计算所有数的和"""
total = sum(args)
return total
# 可以传递任意数量的参数
result1 = add_all(1, 2, 3)
print(result1) # 输出:6
result2 = add_all(1, 2, 3, 4, 5, 6)
print(result2) # 输出:21
result3 = add_all()
print(result3) # 输出:0示例2:打印所有参数
def print_args(*args):
"""打印所有参数"""
print("接收到的参数:")
for i, arg in enumerate(args, 1):
print(f" 参数{i}:{arg}")
print_args("苹果", "香蕉", "橙子", "葡萄")
# 输出:
# 接收到的参数:
# 参数1:苹果
# 参数2:香蕉
# 参数3:橙子
# 参数4:葡萄示例3:寻找最大值
def find_max(*args):
"""找出最大值"""
if len(args) == 0:
return None
return max(args)
print(find_max(3, 1, 4, 1, 5, 9, 2, 6)) # 输出:9
print(find_max(-1, -5, -3)) # 输出:-1三、**kwargs:关键字可变参数
基本语法
def 函数名(**kwargs):
函数体特点
**kwargs在函数内部是一个字典- 可以接收任意数量的关键字参数
- 参数名可以是任意名称(通常用kwargs)
示例4:打印所有关键字参数
def print_kwargs(**kwargs):
"""打印所有关键字参数"""
print("接收到的关键字参数:")
for key, value in kwargs.items():
print(f" {key} = {value}")
print_kwargs(name="小明", age=25, city="北京")
# 输出:
# 接收到的关键字参数:
# name = 小明
# age = 25
# city = 北京
print_kwargs(product="手机", price=2999, stock=100, brand="华为")
# 输出:
# 接收到的关键字参数:
# product = 手机
# price = 2999
# stock = 100
# brand = 华为示例5:构建用户信息
def build_profile(**kwargs):
"""构建用户信息"""
profile = {}
for key, value in kwargs.items():
profile[key] = value
return profile
user1 = build_profile(name="小明", age=25, email="xiaoming@example.com")
print(user1)
# 输出:{'name': '小明', 'age': 25, 'email': 'xiaoming@example.com'}
user2 = build_profile(name="小红", city="上海", hobby="编程")
print(user2)
# 输出:{'name': '小红', 'city': '上海', 'hobby': '编程'}示例6:配置函数
def configure_system(**kwargs):
"""配置系统"""
config = {
"host": "localhost",
"port": 8080,
"debug": False,
"timeout": 30
}
# 更新配置
config.update(kwargs)
print("系统配置:")
for key, value in config.items():
print(f" {key} = {value}")
return config
# 使用默认配置
configure_system()
# 覆盖部分配置
configure_system(host="192.168.1.100", port=9000, debug=True)
# 完全自定义配置
configure_system(host="example.com", port=443, timeout=60, ssl=True)四、混合使用各种参数
参数顺序规则
def func(固定参数, *args, 默认参数, **kwargs):
函数体示例7:混合参数使用
def show_info(name, *args, **kwargs):
"""显示信息"""
print(f"姓名:{name}")
if args:
print("其他参数(元组):")
for i, arg in enumerate(args, 1):
print(f" 参数{i}:{arg}")
if kwargs:
print("关键字参数(字典):")
for key, value in kwargs.items():
print(f" {key}:{value}")
show_info("小明", 25, "北京", gender="男", hobby="编程")
# 输出:
# 姓名:小明
# 其他参数(元组):
# 参数1:25
# 参数2:北京
# 关键字参数(字典):
# gender:男
# hobby:编程示例8:计算器函数
def calculator(operation, *numbers, precision=2):
"""计算器"""
result = 0
if operation == "add":
result = sum(numbers)
elif operation == "multiply":
result = 1
for num in numbers:
result *= num
elif operation == "max":
result = max(numbers) if numbers else 0
elif operation == "min":
result = min(numbers) if numbers else 0
return round(result, precision)
# 使用不同的运算
print(calculator("add", 1, 2, 3, 4, 5)) # 输出:15
print(calculator("multiply", 2, 3, 4)) # 输出:24
print(calculator("max", 5, 2, 8, 1, 9)) # 输出:9
print(calculator("min", 5, 2, 8, 1, 9)) # 输出:1
print(calculator("add", 1.111, 2.222, 3.333, precision=3)) # 输出:6.666五、实际应用案例
案例1:格式化字符串
def format_message(template, *args, **kwargs):
"""格式化消息"""
try:
# 使用位置参数
result = template.format(*args)
# 使用关键字参数替换剩余的占位符
result = result.format(**kwargs)
return result
except Exception as e:
return f"格式化错误:{e}"
# 使用示例
message1 = format_message("你好,{}!", "小明")
print(message1) # 输出:你好,小明!
message2 = format_message("姓名:{},年龄:{},城市:{}",
"小红", 25, city="上海")
print(message2) # 输出:姓名:小红,年龄:25,城市:上海案例2:购物车
def shopping_cart(customer_name, *items, **options):
"""购物车"""
print(f"客户:{customer_name}")
print("\n商品清单:")
total = 0
for i, item in enumerate(items, 1):
if isinstance(item, dict):
# 商品是字典格式
name = item.get("name", "未知")
price = item.get("price", 0)
quantity = item.get("quantity", 1)
subtotal = price * quantity
total += subtotal
print(f" {i}. {name} × {quantity} = ¥{subtotal}")
else:
# 简单的商品名称
print(f" {i}. {item}")
print(f"\n小计:¥{total}")
# 应用优惠
discount = options.get("discount", 0)
if discount > 0:
discount_amount = total * discount
total -= discount_amount
print(f"折扣:-¥{discount_amount:.2f}")
print(f"总计:¥{total:.2f}")
# 使用示例
shopping_cart("小明",
{"name": "手机", "price": 2999, "quantity": 1},
{"name": "耳机", "price": 199, "quantity": 2},
discount=0.1)
shopping_cart("小红",
"笔记本", "笔", "橡皮擦")案例3:日志记录器
def log(message, *args, level="INFO", timestamp=True, **metadata):
"""日志记录器"""
from datetime import datetime
# 构建日志信息
log_entry = []
# 添加时间戳
if timestamp:
time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry.append(f"[{time_str}]")
# 添加日志级别
log_entry.append(f"[{level}]")
# 添加消息
if args:
message = message.format(*args)
log_entry.append(f" {message}")
# 添加元数据
if metadata:
metadata_str = ", ".join(f"{k}={v}" for k, v in metadata.items())
log_entry.append(f" ({metadata_str})")
# 输出日志
print("".join(log_entry))
# 使用示例
log("用户 {} 登录成功", "xiaoming", level="INFO")
log("数据库连接失败", level="ERROR", host="localhost", port=5432)
log("订单 {} 创建成功,金额:{}", "ORDER123", 299.99,
level="DEBUG", user_id=1001)六、解包操作
解包列表/元组给*args
def add_all(*args):
"""计算所有数的和"""
return sum(args)
numbers = [1, 2, 3, 4, 5]
result = add_all(*numbers) # 解包列表
print(result) # 输出:15解包字典给**kwargs
def show_info(**kwargs):
"""显示信息"""
for key, value in kwargs.items():
print(f"{key}: {value}")
user_data = {"name": "小明", "age": 25, "city": "北京"}
show_info(**user_data)
# 输出:
# name: 小明
# age: 25
# city: 北京混合解包
def process_data(name, age, *hobbies, **details):
"""处理数据"""
print(f"姓名:{name}")
print(f"年龄:{age}")
if hobbies:
print("爱好:")
for hobby in hobbies:
print(f" - {hobby}")
if details:
print("详情:")
for key, value in details.items():
print(f" {key}: {value}")
base_info = ["小明", 25]
extra_hobbies = ["编程", "阅读", "游泳"]
extra_details = {"city": "北京", "job": "工程师"}
process_data(*base_info, *extra_hobbies, **extra_details)七、可变参数的最佳实践
实践1:合理命名
# ✅ 好的命名
def print_values(*numbers):
"""打印多个数字"""
print(numbers)
def get_person_info(**person_data):
"""获取人员信息"""
return person_data
# ❌ 不好的命名(虽然可以工作,但不直观)
def print_values(*a):
"""打印多个数字"""
print(a)实践2:提供文档说明
def aggregate_data(operation, *values, **options):
"""
聚合数据
参数:
operation (str): 操作类型(sum, avg, max, min)
*values: 任意数量的数值
**options: 可选参数
- precision (int): 精度,默认2
- verbose (bool): 是否显示详细信息,默认False
返回:
计算结果
"""
pass实践3:参数验证
def calculate_average(*numbers, precision=2):
"""计算平均值"""
if len(numbers) == 0:
return 0
# 验证所有参数都是数字
if not all(isinstance(num, (int, float)) for num in numbers):
raise ValueError("所有参数必须是数字")
average = sum(numbers) / len(numbers)
return round(average, precision)
# 使用示例
print(calculate_average(1, 2, 3, 4, 5)) # 输出:3.0
# print(calculate_average(1, 2, "3")) # 报错八、常见错误与调试
错误1:*args和**kwargs混用错误
# ❌ 错误:**kwargs必须在*args之后
def func(*args, **kwargs, name): # 报错
pass
# ✅ 正确
def func(*args, name, **kwargs):
pass错误2:重复定义参数
# ❌ 错误
def func(name, *args, name): # 报错:name重复定义
pass
# ✅ 正确
def func(name, *args, age): # 不同参数名
pass调试技巧:打印参数
def debug_func(*args, **kwargs):
"""调试函数"""
print(f"args 类型:{type(args)}")
print(f"args 内容:{args}")
print(f"kwargs 类型:{type(kwargs)}")
print(f"kwargs 内容:{kwargs}")
debug_func(1, 2, 3, name="小明", age=25)九、可变参数的优势
| 优势 | 说明 |
|---|---|
| 灵活性 | 可以处理任意数量的参数 |
| 通用性 | 适用于各种场景 |
| 简化调用 | 不需要提前知道参数数量 |
| 可扩展 | 易于添加新功能 |
十、小结
| 知识点 | 说明 |
|---|---|
| *args | 位置可变参数,内部是元组 |
| **kwargs | 关键字可变参数,内部是字典 |
| 参数顺序 | 固定参数 → *args → 默认参数 → **kwargs |
| 解包 | 使用*和**解包列表和字典 |
| 混合使用 | 可以同时使用多种参数类型 |
十一、课后练习
练习1
定义函数multiply_all(*args),计算所有数的乘积。
练习2
定义函数build_person(**kwargs),构建人员信息字典。
练习3
定义函数format_report(title, *data, **options),生成报告。
练习4
定义函数find_middle(*args),找出中间位置的数。
练习5
定义函数create_email(to, subject, *cc_list, **headers),创建邮件信息。