第2章 2.3 函数定义与参数

🎯 开场 3 分钟:为什么要学这个?

上一章我们学会了让电脑"重复干活"——for 循环让小明不用手工算 100 个数的累加,while 循环让你不用手动点赞就能刷完朋友圈。但问题来了:如果明天老板让你算的不是 100 个数,而是 1000 个,或者让你算"最大值的 3 倍减 5"呢?

你肯定不想每次都回去改 for 循环里的代码逻辑。

这就引出了今天的主角——函数。函数就像一个会自动工作的流水线机器:你把原材料(参数)扔进去,它自动产出成品(返回值),不用你盯着每个步骤。

学完这章,你能:
- 写出自己的"小机器",告别复制粘贴
- 让代码复用起来,改一处,全调用都生效
- 读懂别人代码里的函数,理解参数和返回值怎么回事


🧱 基础 25 分钟:核心概念

什么是函数?"代码收纳盒"

想象你妈做饭:备料 → 切菜 → 炒菜 → 装盘,每步她不用教别人,别人也知道干嘛——因为"炒菜"这个动作是固定的。

函数就是这样一组打包好的动\n\nSimple tech illustration expla\n\nAI comic creation scene, creat\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.060.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 个核心点

  1. 函数是代码的"收纳盒"def 定义,return 返回,告别复制粘贴
  2. 参数是原材料:位置参数看顺序,默认参数有默认值,可变参数 *args 接收任意数量
  3. 拆函数就是拆任务:每个函数只做一件事,组合起来能对付复杂需求

推荐延伸学习资源

  1. Python 官方文档:函数定义 —— 最权威,用好 *args**kwargs
  2. 《Python 编程:从入门到实践》第 8 章 —— 有更多实战练习
  3. Real Python: 函数进阶 —— 深入理解参数传递机制

互动钩子

你在工作或生活中用过函数吗? 比如自动整理文件、批量发消息、算账目?评论区聊聊你遇到过的问题,老粉优先回复!


📌 下章剧透:函数学会了,但变量怎么"藏起来"不被乱改?下一章我们聊作用域——Python 是怎么管理谁的变量谁用的?还有匿名函数(lambda),让你写"一次性小机器"不用起名字。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。