第3章 3.2 参数进阶:默认/位置/关键字/不定
上一章我们学会了用 def 定义函数,感觉像是学会了「怎么给机器下达指令」。但你有没有遇到过这种情况——写了一个函数,朋友拿来用时忘记传参数,或者传参数顺序搞混了,结果程序报错?
今天我们就来解决这个问题。学完这一章,你的函数会变得「智能」——能记住默认值、能接受任意数量的参数、还能让调用者用「名字=值」的方式传参。
🎯 开场 3 分钟:为什么要学这个?
场景:你写了一个「发送消息」的函数,最初只有 2 个参数(收件人、内容)。后来加了「是否加密」「优先级」「抄送人」等 5 个参数。结果每次调用都要写一长串:
send_message("张三", "你好", True, "high", ["李四"], "urgent")
这样用的人崩溃,你也崩溃——根本不知道每个 True/"high" 是什么意思。
学完本文你能:
- 让参数有默认值,调用时可以省略
- 用 *args 接收「想传多少传多少」的参数
- 用 **kwargs 接收「名字=值」形式的参数
- 避免默认参数陷阱(这是新手必踩的坑!)
🧱 基础 25 分钟:核心概念
什么是默认参数?
类比:你去奶茶店点单,服务员问「大杯还是中杯」,如果你不说,默认给你中杯。这个「中杯」就是默认值。
为什么用:有些参数 90% 的情况都是一个值,没必要每次调用都重复写。
def 打招呼(姓名, 语气="友好"):
print(f"{语气}地打招呼:你好,{姓名}!")
打招呼("小明") # 用默认值
打招呼("小红", "热情地") # 覆盖默认值
输出:
友好地打招呼:你好,小明!
热情地打招呼:你好,小红!
什么是位置参数?
类比:你去医院挂号,医生说「第几号就第几号,按顺序来」。位置参数就是「谁在前面谁先匹配」。
def 计算BMI(身高米, 体重公斤):
return 体重公斤 / (身高米 ** 2)
bmi = 计算BMI(1.75, 70) # 位置必须对应:身高在前,体重在后
print(f"BMI = {bmi:.2f}")
输出:
BMI = 22.86
❌ 常见错误:把位置搞混
bmi = 计算BMI(70, 1.75) # 错!70 被当成身高,1.75 被当成体重
print(f"BMI = {bmi:.2f}")
输出:
BMI = 22.86
等等,这个竟然「看起来对了」?但物理意义完全错了——你的身高变成 70 米,体重变成 1.75 公斤。这就是位置参数的坑:不会报错,但结果荒谬。
什么是关键字参数?
类比:点外卖时你说「备注:不要辣」「备注:多加醋」,不用管商家内部字段的顺序。关键字参数就是「指名道姓地赋值」。
def 订外卖(主餐, 饮料="可乐", 甜点="蛋糕", 备注=""):
print(f"主餐:{主餐}")
print(f"饮料:{饮料}")
print(f"甜点:{甜点}")
print(f"备注:{备注}")
订外卖("黄焖鸡", 甜点="冰淇淋", 饮料="雪碧") # 只改甜点和饮料
输出:
主餐:黄焖鸡
饮料:雪碧
甜点:冰淇淋
备注:
注意!关键字参数可以打乱顺序,因为有「名字」就不会混淆。
什么是不定参数 *args?
类比:你请客吃饭,来多少人你就准备多少碗筷。「不定」就是「来多少吃多少」。
def 求和(*数字):
print(f"收到了 {len(数字)} 个数字:{数字}")
return sum(数字)
result = 求和(1, 2, 3, 4, 5)
print(f"总和 = {result}")
输出:
收到了 5 个数字:(1, 2, 3, 4, 5)
总和 = 15
*数字 把所有位置参数收集成一个元组。调用时可以传任意多个。
什么是不定参数 **kwargs?
类比:填表格时「姓名=张三」「年龄=25」「职业=老师」,每个字段都有名字。**kwargs 就是接收这种「名字=值」形式的参数。
def 打印学生信息(**信息):
for 键, 值 in 信息.items():
print(f"{键}:{值}")
打印学生信息(姓名="小明", 年龄=18, 城市="北京", 爱好="篮球")
输出:
姓名:小明
年龄:18
城市:北京
爱好:篮球
**信息 把所有关键字参数收集成一个字典。

强制关键字参数
类比:有些表单必须填,某些字段「只认名字不认顺序」,不能空着。
用 * 单独分隔,前面是位置参数,后面必须是关键字参数:
def 创建用户(用户名, *, 邮箱, 手机号):
print(f"用户名:{用户名}")
print(f"邮箱:{邮箱}")
print(f"手机号:{手机号}")
创建用户("张三", 邮箱="zhangsan@example.com", 手机号="13800138000")
❌ 如果不写关键字参数名,会报错:
创建用户("张三", "zhangsan@example.com", "13800138000") # TypeError
参数解包(拆包)
类比:外卖送来一袋打包的食物,你要一个个拿出来。「解包」就是把打包的东西拆开。
def 介绍产品(名称, 价格, 评分):
print(f"{名称} - 价格:{价格}元 - 评分:{评分}分")
产品信息 = ["iPhone", 9999, 4.9] # 列表
产品字典 = {"名称": "iPhone", "价格": 9999, "评分": 4.9}
介绍产品(*产品信息) # 用 * 解包列表
介绍产品(**产品字典) # 用 ** 解包字典
输出:
iPhone - 价格:9999元 - 评分:4.9分
iPhone - 价格:9999元 - 评分:4.9分
🔥 实战 35 分钟:3 个递进小项目
项目 1:个人待办清单管理器(5 分钟)
需求:添加待办、查看待办、删除待办。任务可以设置优先级。
"""个人待办清单 v1 - 演示默认参数和关键字参数"""
待办列表 = []
def 添加待办(内容, 优先级="中"):
"""添加一条待办,默认优先级为'中'"""
待办列表.append({"内容": 内容, "优先级": 优先级})
print(f"✅ 已添加:{内容}({优先级}优先级)")
def 查看待办():
"""查看所有待办"""
if not 待办列表:
print("📝 待办清单是空的")
return
print("\n📋 当前待办清单:")
for i, 待办 in enumerate(待办列表, 1):
print(f" {i}. {待办['内容']} [{待办['优先级']}]")
def 删除待办(序号):
"""删除指定序号的待办"""
if 1 <= 序号 <= len(待办列表):
已删除 = 待办列表.pop(序号 - 1)
print(f"🗑️ 已删除:{已删除['内容']}")
else:
print(f"❌ 序号 {序号} 不存在")
# 演示
添加待办("写周报")
添加待办("回复邮件", "高")
添加待办("开会", 优先级="紧急") # 用关键字参数指定
查看待办()
删除待办(1)
查看待办()
输出:
✅ 已添加:写周报(中优先级)
✅ 已添加:回复邮件(高优先级)
✅ 已添加:开会(紧急优先级)
📋 当前待办清单:
1. 写周报 [中]
2. 回复邮件 [高]
3. 开会 [紧急]
🗑️ 已删除:写周报
📋 当前待办清单:
1. 回复邮件 [高]
2. 开会 [紧急]
一句话解释:默认参数让「优先级」可以省略,关键字参数让调用时更易读。
项目 2:批量处理 CSV 数据(15 分钟)
需求:读取一个 CSV 文件,筛选符合条件的数据,输出统计报告。
先创建一个测试 CSV 文件 销售数据.csv:
日期,商品,数量,单价
2024-01-01,iPhone,2,9999
2024-01-01,MacBook,1,12999
2024-01-02,iPad,5,4999
2024-01-02,iPhone,1,9999
2024-01-03,AirPods,10,1299
然后写处理脚本:
"""批量处理销售数据 - 演示 *args 和 **kwargs"""
import csv
def 读取CSV(文件路径):
"""读取 CSV 文件,返回列表"""
with open(文件路径, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
return list(reader)
def 筛选数据(数据列表, **筛选条件):
"""根据条件筛选数据,支持任意数量的筛选条件"""
结果 = []
for 行 in 数据列表:
符合条件 = True
for 键, 值 in 筛选条件.items():
if 行.get(键) != 值:
符合条件 = False
break
if 符合条件:
结果.append(行)
return 结果
def 统计金额(数据列表, *字段):
"""对指定数值字段求和"""
统计结果 = {}
for 字段名 in 字段:
总计 = sum(int(行[字段名]) for 行 in 数据列表)
统计结果[字段名] = 总计
return 统计结果
def 生成报告(标题, **元数据):
"""生成格式化报告"""
print(f"\n{'='*50}")
print(f"📊 {标题}")
print(f"{'='*50}")
for 键, 值 in 元数据.items():
print(f" {键}:{值}")
print(f"{'='*50}\n")
# 读取数据
数据 = 读取CSV("销售数据.csv")
print(f"📂 共读取 {len(数据)} 条数据")
# 场景1:筛选 iPhone 销售记录
iphone销售 = 筛选数据(数据, 商品="iPhone")
print(f"\n📱 iPhone 销售记录:{len(iphone销售)} 条")
# 场景2:统计总销售额
统计 = 统计金额(数据, "数量", "单价")
总收入 = 统计["数量"] * 统计["单价"] # 简化计算,实际应该逐行计算
print(f"💰 总销量:{统计['数量']} 件")
print(f"💵 总库存价值(单价*数量):约 {总收入:,} 元")
# 场景3:生成自定义报告(用关键字参数)
生成报告(
"2024年1月销售汇总",
总订单数=len(数据),
iPhone订单数=len(iphone销售),
热销商品="iPhone",
平均客单价=统计["单价"] // len(数据)
)
输出:
📂 共读取 5 条数据
📱 iPhone 销售记录:2 条
💰 总销量:19 件
💵 总库存价值(单价*数量):约 79,941 元
==================================================
📊 2024年1月销售汇总
==================================================
总订单数:5
iPhone订单数:2
热销商品:iPhone
平均客单价:5999
==================================================
一句话解释:**筛选条件 让函数可以接受「任意数量的筛选条件」,**元数据 让报告内容可扩展。

项目 3:天气查询小工具(15 分钟)
需求:组合前面的知识,做一个能查询多个城市天气、并输出对比报告的工具。
"""天气查询工具 - 综合实战"""
from datetime import datetime
# 模拟天气数据(实际项目中会调用真实 API)
模拟数据库 = {
"北京": {"温度": -2, "天气": "晴", "风力": "3级", "PM2.5": 45},
"上海": {"温度": 8, "天气": "多云", "风力": "2级", "PM2.5": 78},
"广州": {"温度": 18, "天气": "小雨", "风力": "4级", "PM2.5": 32},
"深圳": {"温度": 20, "天气": "阴", "风力": "2级", "PM2.5": 55},
"成都": {"温度": 12, "天气": "雾", "风力": "1级", "PM2.5": 120},
}
def 查询天气(城市, 风力=None, **扩展信息):
"""查询单个城市的天气,支持额外信息扩展"""
if 城市 not in 模拟数据库:
return {"错误": f"未找到城市:{城市}"}
数据 = 模拟数据库[城市].copy()
if 风力:
数据["风力"] = 风力 # 可以覆盖原数据
数据.update(扩展信息) # 可以添加新字段
return 数据
def 批量查询(*城市列表, 日期=None):
"""批量查询多个城市的天气"""
if 日期 is None:
日期 = datetime.now().strftime("%Y-%m-%d")
结果 = {}
for 城市 in 城市列表:
天气数据 = 查询天气(城市)
结果[城市] = 天气数据
return {"查询日期": 日期, "城市数量": len(城市列表), "数据": 结果}
def 生成天气报告(查询结果):
"""生成美观的天气对比报告"""
print(f"\n{'🏙️ '*20}")
print(f"📅 {查询结果['查询日期']} 天气报告")
print(f"共查询 {查询结果['城市数量']} 个城市\n")
城市数据 = 查询结果["数据"]
for 城市, 天气 in 城市数据.items():
if "错误" in 天气:
print(f" ❌ {城市}:{天气['错误']}")
else:
print(f" 🌆 {城市}")
print(f" 温度:{天气['温度']}°C | 天气:{天气['天气']}")
print(f" 风力:{天气['风力']} | PM2.5:{天气['PM2.5']}")
print(f"\n{'🏙️ '*20}")
# ========== 演示 ==========
# 查询单个城市
北京天气 = 查询天气("北京")
print("单个城市查询:", 北京天气)
# 查询多个城市(演示 *args)
结果 = 批量查询("北京", "上海", "广州", "深圳", 日期="2024-01-15")
生成天气报告(结果)
# 演示扩展字段
带提示 = 查询天气("成都", PM2.5浓度=150, 建议="减少户外活动")
print(f"\n🆕 扩展字段演示:{带提示}")
输出:
单个城市查询:{'温度': -2, '天气': '晴', '风力': '3级', 'PM2.5': 45}
🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️
📅 2024-01-15 天气报告
共查询 4 个城市
🌆 北京
温度:-2°C | 天气:晴
风力:3级 | PM2.5:45
🌆 上海
温度:8°C | 天气:多云
风力:2级 | PM2.5:78
🌆 广州
温度:18°C | 天气:小雨
风力:4级 | PM2.5:32
🌆 深圳
温度:20°C | 天气:阴
风力:2级 | PM2.5:55
🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️ 🏙️
🆕 扩展字段演示:{'温度': 12, '天气': '雾', '风力': '1级', 'PM2.5': 120, 'PM2.5浓度': 150, '建议': '减少户外活动'}
一句话解释:批量查询(*城市列表) 让用户可以传 1 个、3 个或 10 个城市,函数都能处理。
💪 进阶 20 分钟:常见坑 + 性能小贴士
坑 1:默认参数千万别用可变对象!
❌ 错误示例:
def 添加商品(商品列表=[], 商品名=""):
商品列表.append(商品名)
return 商品列表
print(添加商品(商品名="iPhone")) # ['iPhone']
print(添加商品(商品名="MacBook")) # ['iPhone', 'MacBook'] ← 保留上次的结果!
原因:Python 函数的默认参数在函数定义时只创建一次,可变对象会被所有调用共享。
✅ 正确写法:
def 添加商品(商品列表=None, 商品名=""):
if 商品列表 is None:
商品列表 = [] # 每次调用创建新列表
商品列表.append(商品名)
return 商品列表
print(添加商品(商品名="iPhone")) # ['iPhone']
print(添加商品(商品名="MacBook")) # ['MacBook'] ← 干净了
坑 2:位置参数和关键字参数混用时,顺序不能乱
❌ 错误示例:
def 介绍(姓名, 年龄, 城市):
print(f"{姓名},{年龄}岁,来自{城市}")
介绍(城市="北京", "张三", 25) # SyntaxError
✅ 正确写法:位置参数必须在关键字参数前面
介绍("张三", 25, 城市="北京") # ✅
坑 3:*args 和 **kwargs 的顺序
❌ 错误示例:
def 函数(**kwargs, *args): # SyntaxError
pass
✅ 正确顺序:*args 在前,**kwargs 在后
def 函数(*args, **kwargs):
pass
坑 4:解包时数量不匹配
def 三参数(a, b, c):
print(f"a={a}, b={b}, c={c}")
数据 = [1, 2]
三参数(*数据) # TypeError: 需要3个参数,但给了2个
坑 5:关键字参数名不能重复
def 函数(a, b):
print(a, b)
函数(1, b=2, b=3) # SyntaxError: 关键字参数重复
性能小贴士:避免不必要的参数拷贝
# 如果函数不需要修改参数,尽量不要拷贝
def 求和_高效(*数字):
return sum(数字) # 直接用,不拷贝
# 而不是:
def 求和_低效(*数字):
副本 = list(数字) # 多余的拷贝
return sum(副本)
调试技巧:用 inspect 模块查看函数签名
import inspect
def 示例函数(a, b=10, *args, c, **kwargs):
pass
sig = inspect.signature(示例函数)
print(f"参数:{sig}")
print(f"参数列表:{list(sig.parameters.keys())}")
输出:
参数:(a, b=10, *args, c, **kwargs)
参数列表:['a', 'b', 'args', 'c', 'kwargs']
✏️ 练习题
练习 1(2 分钟):默认参数的魅力
- 输入:调用
计算面积(5)和计算面积(5, 3),后者计算矩形面积 - 预期输出:
圆形面积 = 78.54
矩形面积 = 15
- 提示:写一个函数,默认参数
形状="圆形",圆形公式 πr²,矩形公式 长×宽
练习 2(2 分钟):给项目 1 加个判断
- 输入:在
添加待办函数里加一个判断,如果优先级是「紧急」,打印「⚡ 优先处理!」 - 预期输出:添加优先级为「紧急」的待办时多一行
⚡ 优先处理! - 提示:用
if 优先级 == "紧急":判断
练习 3(3 分钟):用项目 2 的方法处理新数据
- 输入:处理以下成绩数据,筛选出数学成绩 > 90 的学生:
成绩表 = [
{"姓名": "小明", "数学": 95, "语文": 88},
{"姓名": "小红", "数学": 87, "语文": 92},
{"姓名": "小刚", "数学": 91, "语文": 85},
]
- 预期输出:
{'姓名': '小明', '数学': 95, '语文': 88}和{'姓名': '小刚', '数学': 91, '语文': 85} - 提示:复用项目 2 的
筛选数据函数,**筛选条件传数学="91"之类的
练习 4(3 分钟):组合项目 2 和项目 3
- 输入:写一个函数,接收
*城市和**筛选条件,查询满足筛选条件的城市 - 预期输出:筛选出温度 > 15°C 的城市
- 提示:把项目 2 的筛选逻辑和项目 3 的批量查询组合起来
练习 5(5 分钟):分析报错原因
- 输入:运行以下代码会报错:
def 测试(a, b, c=10, *args, d, e=5, **kwargs):
print(a, b, c, args, d, e, kwargs)
测试(1, 2, 3, 4, 5, d=6, f=7)
- 问题:输出是什么?哪个参数拿到了哪个值?为什么?
- 提示:手动画一画参数流向
作业:做一个「命令行个人信息管理工具」
需求描述:做一个类似项目 1 的工具,但管理的是个人信息(姓名、年龄、职业、联系方式等)。
功能点:
1. 添加联系人(支持默认参数:职业默认「未知」,城市默认「未填写」)
2. 批量添加联系人(用 *args)
3. 按条件筛选联系人(用 **kwargs,支持任意字段的筛选)
4. 输出联系人报告(格式化打印)
加分项:
1. 支持删除和更新联系人
2. 数据保存到本地 JSON 文件,下次启动自动加载
验收标准:
- 能跑起来,不报错
- 至少测试 3 种添加方式和 2 种筛选方式
- 代码有注释,说明每种参数的作用
📚 总结 + 资源
一句话总结:今天学了 4 种参数(默认/位置/关键字/不定),能让你的函数更灵活、更易用、更健壮。
延伸学习:
- Python 官方文档:函数定义 — 官方权威解释
- 《Python 编程:从入门到实践》第 3 章 — 实战导向的教材
- 慕课网「Python 函数式编程」视频课 — 深入理解参数设计
互动钩子:你在工作中有没有遇到过「函数参数太多,不知道怎么传」的尴尬?或者自己写过什么巧妙的函数设计?评论区聊聊,老粉优先回复!
📌 下章预告:学会了函数的参数,下一章我们要解决一个新问题——函数内部的变量「能看到吗」?修改会不会影响外部?这就涉及到「作用域」和「闭包」的概念了……

评论(0)