第2章 2.3 函数定义与参数
🎯 开场 3 分钟:为什么要学这个?
上一章我们学会了让电脑"重复干活"——for 循环让小明不用手工算 100 个数的累加,while 循环让你不用手动点赞就能刷完朋友圈。但问题来了:如果明天老板让你算的不是 100 个数,而是 1000 个,或者让你算"最大值的 3 倍减 5"呢?
你肯定不想每次都回去改 for 循环里的代码逻辑。
这就引出了今天的主角——函数。函数就像一个会自动工作的流水线机器:你把原材料(参数)扔进去,它自动产出成品(返回值),不用你盯着每个步骤。
学完这章,你能:
- 写出自己的"小机器",告别复制粘贴
- 让代码复用起来,改一处,全调用都生效
- 读懂别人代码里的函数,理解参数和返回值怎么回事
🧱 基础 25 分钟:核心概念
什么是函数?"代码收纳盒"
想象你妈做饭:备料 → 切菜 → 炒菜 → 装盘,每步她不用教别人,别人也知道干嘛——因为"炒菜"这个动作是固定的。
函数就是这样一组打包好的动\n\n
\n\n
\n\n作,给它一个名字,需要的时候喊名字就行,不用每次都重新说"先把锅烧热,再放油,再放菜..."
# 这就是一个函数:计算圆的面积
def 计算圆形面积(半径):
面积 = 3.14 * 半径 * 半径
return 面积
# 调用它
结果 = 计算圆形面积(5)
print(结果) # 输出: 78.5
def 就是"定义"(define),return 就是"返回结果"。
参数:函数的"原材料入口"
位置参数——按顺序放进去,就像填空题
def 打招呼(姓名, 职业):
return f"您好,{姓名}!您的职业是{职业}。"
print(打招呼("小明", "程序员")) # 输出: 您好,小明!您的职业是程序员。
默认参数——如果没特别说明,就用默认值,就像外卖默认微辣
def 订餐(主菜, 口味="微辣"):
return f"您点了{main菜},口味{口味}。"
print(订餐("宫保鸡丁")) # 输出: 您点了宫保鸡丁,口味微辣。
print(订餐("火锅", "特辣")) # 输出: 您点了火锅,口味特辣。
可变参数——材料数量不确定,就像聚餐不知道来几个人
def 求总和(*数字们):
结果 = 0
for 数字 in 数字们:
结果 += 数字
return 结果
print(求总和(1, 2, 3)) # 输出: 6
print(求总和(10, 20, 30, 40, 50)) # 输出: 150
!*数字们 把所有参数打包成一个"数字列表",不管来几个都能收。
返回值:函数的"成品出口"
return 就是交作业——告诉调用者"我算完了,这是结果"。
def 计算税费(金额, 税率=0.13):
税费 = 金额 * 税率
return 税费
实付 = 100 + 计算税费(100)
print(f"实付金额: {实付}") # 输出: 实付金额: 113.0
没有 return 的函数——返回 None,就像点外卖没收到货
def 只打印不返回(文字):
print(文字)
# 没有 return
结果 = 只打印不返回("你好")
print(结果) # 输出: None
类型声明:给机器的操作说明书
Python 3.5+ 可以声明参数和返回值的类型,虽然不强制,但能让别人看懂你的代码在干嘛。
def 计算平均值(成绩列表: list[float]) -> float:
if not 成绩列表:
return 0.0
return sum(成绩列表) / len(成绩列表)
平均分 = 计算平均值([85, 90, 78, 92])
print(f"平均分: {平均分}") # 输出: 平均分: 86.25
: list[float] 意思是"期望传入一个浮点数列表",-> float 意思是"返回浮点数"。
🔥 实战 35 分钟:3 个递进的小项目
项目 1(5 分钟):购物小票计算器
需求:计算小明的购物总价,会员打 9 折。
# 项目1:购物小票计算器
def 计算商品小计(单价, 数量):
"""计算单个商品的小计金额"""
return 单价 * 数量
def 应用会员折扣(总价, 是会员=False):
"""根据会员身份应用折扣"""
if 是会员:
return 总价 * 0.9
return 总价
# 小明的购物清单
购物车 = [
{"商品": "面包", "单价": 5.5, "数量": 3},
{"商品": "牛奶", "单价": 4.2, "数量": 2},
{"商品": "鸡蛋", "单价": 12.8, "数量": 1},
]
总价 = 0
for 商品 in 购物车:
小计 = 计算商品小计(商品["单价"], 商品["数量"])
print(f"{商品['商品']}: {小计}元")
总价 += 小计
最终价 = 应用会员折扣(总价, 是会员=True)
print(f"\n原价: {总价}元")
print(f"会员价: {最终价}元")
预期输出:
面包: 16.5元
牛奶: 8.4元
鸡蛋: 12.8元
原价: 37.7元
会员价: 33.93元
一句话解释:我们把"算小计"和"打折"分别做成函数,这样改折扣规则时只用改一个地方。
项目 2(15 分钟):学生成绩统计表
需求:从 CSV 数据里读取学生成绩,统计每个学生的平均分、最高分,标记是否及格。
# 项目2:学生成绩统计表
def 计算平均分(成绩列表: list[float]) -> float:
"""计算成绩列表的平均值"""
if not 成绩列表:
return 0.0
return sum(成绩列表) / len(成绩列表)
def 获取最高分(成绩列表: list[float]) -> float:
"""返回最高分"""
if not 成绩列表:
return 0.0
return max(成绩列表)
def 判断是否及格(平均分: float, 及格线: float = 60.0) -> str:
"""判断是否及格"""
return "✅ 及格" if 平均分 >= 及格线 else "❌ 不及格"
# 模拟的学生数据(实际项目可以从CSV读取)
学生数据 = [
{"姓名": "小明", "语文": 85, "数学": 92, "英语": 78},
{"姓名": "小红", "语文": 91, "数学": 88, "英语": 95},
{"姓名": "小刚", "语文": 72, "数学": 65, "英语": 58},
{"姓名": "小丽", "语文": 88, "数学": 76, "英语": 82},
]
# 统计每个学生
for 学生 in 学生数据:
成绩列表 = [学生["语文"], 学生["数学"], 学生["英语"]]
平均分 = 计算平均分(成绩列表)
最高分 = 获取最高分(成绩列表)
状态 = 判断是否及格(平均分)
print(f"\n{学生['姓名']}:")
print(f" 成绩: 语{学生['语文']} 数{学生['数学']} 英{学生['英语']}")
print(f" 平均分: {平均分:.1f} | 最高分: {最高分}")
print(f" 状态: {状态}")
预期输出:
小明:
成绩: 语85 数92 英78
平均分: 85.0 | 最高分: 92
状态: ✅ 及格
小红:
成绩: 语91 数88 英95
平均分: 91.3 | 最高分: 95
状态: ✅ 及格
小刚:
成绩: 语72 数65 英58
平均分: 65.0 | 最高分: 72
状态: ✅ 及格
小丽:
成绩: 语88 数76 英82
平均分: 82.0 | 最高分: 88
状态: ✅ 及格
一句话解释:把"算平均分""找最高分""判断及格"拆成独立函数,主循环只负责读数据和展示。
项目 3(15 分钟):个人待办清单管理器
需求:用函数组合实现一个简易待办清单,支持添加、查看、完成、删除操作。
# 项目3:个人待办清单管理器
def 添加待办(清单: list, 事项: str):
"""添加一个新的待办事项"""
清单.append({"事项": 事项, "完成": False})
print(f"✅ 已添加: {事项}")
def 查看待办(清单: list):
"""显示所有待办事项"""
if not 清单:
print("📋 待办清单是空的!")
return
print("\n📋 当前待办清单:")
for 序号, 条目 in enumerate(清单, 1):
状态 = "☑️" if 条目["完成"] else "⬜"
标记 = " [已完成]" if 条目["完成"] else ""
print(f" {序号}. {状态} {条目['事项']}{标记}")
def 完成待办(清单: list, 序号: int) -> bool:
"""标记指定序号的事项为完成"""
if 0 < 序号 <= len(清单):
清单[序号 - 1]["完成"] = True
print(f"🎉 已完成: {清单[序号 - 1]['事项']}")
return True
print("❌ 无效的序号")
return False
def 删除待办(清单: list, 序号: int) -> bool:
"""删除指定序号的事项"""
if 0 < 序号 <= len(清单):
已删除 = 清单.pop(序号 - 1)
print(f"🗑️ 已删除: {已删除['事项']}")
return True
print("❌ 无效的序号")
return False
def 统计完成率(清单: list) -> float:
"""计算完成率"""
if not 清单:
return 0.0
已完成数 = sum(1 for 条目 in 清单 if 条目["完成"])
return (已完成数 / len(清单)) * 100
# 演示使用
我的待办 = []
添加待办(我的待办, "复习第2章函数内容")
添加待办(我的待办, "完成练习题1-3")
添加待办(我的待办, "整理笔记")
添加待办(我的待办, "写作业:做一个实战工具")
查看待办(我的待办)
完成待办(我的待办, 2)
完成待办(我的待办, 3)
删除待办(我的待办, 4)
查看待办(我的待办)
完成率 = 统计完成率(我的待办)
print(f"\n📊 完成率: {完成率:.0f}%")
预期输出:
✅ 已添加: 复习第2章函数内容
✅ 已添加: 完成练习题1-3
✅ 已添加: 整理笔记
✅ 已添加: 写作业:做一个实战工具
📋 当前待办清单:
1. ⬜ 复习第2章函数内容
2. ⬜ 完成练习题1-3
3. ⬜ 整理笔记
4. ⬜ 写作业:做一个实战工具
🎉 已完成: 完成练习题1-3
🎉 已完成: 整理笔记
🗑️ 已删除: 写作业:做一个实战工具
📋 当前待办清单:
1. ⬜ 复习第2章函数内容
2. ☑️ 完成练习题1-3 [已完成]
3. ☑️ 整理笔记 [已完成]
📊 完成率: 67%
一句话解释:每个操作都是独立函数,主程序只是调用它们——以后想加"优先级"或"截止日期"功能,直接新建函数就行,不用动主逻辑。
💪 进阶 20 分钟:常见坑 + 性能小贴士
坑 1:默认参数别用可变对象!
❌ 错误示例——列表被"共享"了:
def 添加商品(商品, 购物车=[]): # 坑!默认参数是列表,可变!
购物车.append(商品)
return 购物车
print(添加商品("面包")) # ['面包']
print(添加商品("牛奶")) # ['面包', '牛奶'] ← 之前的面包还在!
✅ 正确做法——用 None 做默认值:
def 添加商品(商品, 购物车=None):
if 购物车 is None:
购物车 = []
购物车.append(商品)
return 购物车
print(添加商品("面包")) # ['面包']
print(添加商品("牛奶")) # ['牛奶'] ← 干净的新列表
坑 2:参数顺序搞混
❌ 错误示例:
def 打印信息(姓名, 年龄):
print(f"{姓名} {年龄}岁")
打印信息(25, "小明") # 输出: 25 小明岁 ← 完全不对!
✅ 正确做法——用关键字参数,顺序无所谓:
打印信息(姓名="小明", 年龄=25) # 输出: 小明 25岁
打印信息(年龄=25, 姓名="小明") # 输出: 小明 25岁 ← 换顺序也行
坑 3:忘记 return 返回 None
def 求最大值(数字列表):
# 找到了,但没写 return
for 数字 in 数字列表:
if 数字 > 最大值: # 这行会报错:'最大值' 未定义
最大值 = 数字
结果 = 求最大值([1, 5, 3])
print(结果) # None ← 因为没有 return
✅ 正确做法:
def 求最大值(数字列表):
最大值 = 数字列表[0] # 先初始化
for 数字 in 数字列表:
if 数字 > 最大值:
最大值 = 数字
return 最大值 # 一定要 return
坑 4:变量名和参数名"打架"
总数 = 100
def 计算折扣(总价):
return 总价 * 0.9
折后价 = 计算折扣(总数)
print(折后价) # 90 ← 这里没问题,因为参数是局部变量
# 但如果函数内部也用了 总数:
def 计算折扣(总价):
总数 = 200 # 这会创建新的局部变量,不会影响外面的总数
return 总价 * 0.9
坑 5:可变参数顺序搞错
def 打印学生信息(姓名, *成绩, 城市):
print(f"姓名: {姓名}, 城市: {城市}, 成绩: {成绩}")
打印学生信息("小明", 85, 90, 78, 城市="北京")
# 姓名: 小明, 城市: 北京, 成绩: (85, 90, 78) ← 正确
# 如果漏了关键字参数:
打印学生信息("小红", 92, 88, "上海")
# Python 不知道"上海"是城市还是成绩,会报错!
性能小贴士:避免重复计算
# 低效:每次调用都重新计算
def 判断是否及格(成绩列表):
平均分 = sum(成绩列表) / len(成绩列表) # 算一次
# ... 用到多次平均分
# 优化:先把结果存起来
def 判断是否及格(成绩列表):
平均分 = sum(成绩列表) / len(成绩列表) # 只算一次
是及格 = 平均分 >= 60
return {"平均分": 平均分, "是否及格": 是及格}
调试技巧:print 大法 + 注释临时代码
def 复杂的计算(数字):
结果 = 数字 * 2 + 10
# 调试时打印中间值
print(f"[调试] 输入: {数字}, 中间结果: {结果}")
结果 = 结果 - 5
print(f"[调试] 最终结果: {结果}")
return 结果
✏️ 练习题 + 作业题
练习题(5 道,10 分钟内完成)
练习 1(1 分钟):抄改函数
- 输入:调用 计算税费(1000) 和 计算税费(1000, 0.06)
- 预期输出:130.0 和 60.0
- 提示:上一章项目里的 计算税费 函数支持第二个参数是税率
练习 2(2 分钟):加个判断
- 输入:修改项目 1 的 应用会员折扣 函数,增加"满 100 打 8 折"的规则
- 预期输出:90 元打 9 折(81),120 元不满 100 还是 9 折(108),120 元满 100 打 8 折(96)
- 提示:先判断是否满 100,再决定用哪个折扣
练习 3(2 分钟):新数据统计
- 输入:用项目 2 的函数统计这组成绩:{"姓名": "小张", "语文": 55, "数学": 72, "英语": 80}
- 预期输出:平均分 69.0,最高分 80,状态 ❌ 不及格
- 提示:直接改数据,运行看结果
练习 4(3 分钟):串起两个项目
- 输入:用项目 1 的购物车结构 + 项目 2 的循环方式,统计小明买了 3 样东西的总价和平均单价
- 预期输出:显示每样商品小计,总价,以及"单价平均: X.X 元"
- 提示:复用 计算商品小计 函数,循环外累加,循环后除以数量
练习 5(2 分钟):找 bug
- 输入:下面代码运行后为什么会输出 None?
def 求商(被除数, 除数):
结果 = 被除数 / 除数
# 这里忘了写 return
- 预期输出:解释原因并修复
- 提示:函数没有
return语句会返回什么?
作业题(30 分钟-2 小时)
作业:做一个「个人预算计算器」
小明这个月想买这些东西:
- 房租 2500
- 交通费 300
- 餐饮费 1800
- 购物(衣服+电子产品)2200
- 娱乐(电影+聚餐)600
需求:
1. 定义函数计算每类花费占总花费的百分比
2. 定义函数判断是否超预算(假设总收入 8000)
3. 如果超了,计算超了多少钱,并给出建议("建议减少 X% 的购物开支")
4. 用 *分类费用 可变参数支持任意数量分类
功能点:
1) 接收分类花费(至少 5 类)
2) 计算每类占比,输出格式:房租: 31.25%
3) 显示是否超预算及超支金额
4) 超预算时给出具体建议
加分项:
1) 用类型声明标注参数类型
2) 把结果保存到文件(用 open() 和 write())
验收标准:
- 能跑起来不报错
- 输出格式清晰
- 每个函数有注释说明用途
📚 总结 + 资源
一句话总结本文学到的 3 个核心点
- 函数是代码的"收纳盒":
def定义,return返回,告别复制粘贴 - 参数是原材料:位置参数看顺序,默认参数有默认值,可变参数
*args接收任意数量 - 拆函数就是拆任务:每个函数只做一件事,组合起来能对付复杂需求
推荐延伸学习资源
- Python 官方文档:函数定义 —— 最权威,用好
*args和**kwargs - 《Python 编程:从入门到实践》第 8 章 —— 有更多实战练习
- Real Python: 函数进阶 —— 深入理解参数传递机制
互动钩子
你在工作或生活中用过函数吗? 比如自动整理文件、批量发消息、算账目?评论区聊聊你遇到过的问题,老粉优先回复!
📌 下章剧透:函数学会了,但变量怎么"藏起来"不被乱改?下一章我们聊作用域——Python 是怎么管理谁的变量谁用的?还有匿名函数(lambda),让你写"一次性小机器"不用起名字。

评论(0)