第1章 1.4 函数与模块:从独自作战到团队协作
🎯 开场:先看一个血泪教训
想象一下:小明写了一个 500 行的 Python 程序,所有代码全塞在一个文件里。第 800 行有个 bug,小明改完,第 200 行的功能居然坏了——气得小明把电脑砸了。
你可能也遇到过:代码一长就乱成一锅粥,改这里坏那里。
上一章我们学了循环和判断,能处理批量数据了。但光会「一个人干活」不够,真正厉害的程序员懂得「分工合作」。
学完这一章,你就能:
- 把大程序拆成小零件,想用哪个就用哪个
- 别人写好的工具,直接「拿来主义」
- 代码复用率提升 10 倍,改 bug 不用翻遍全文件
🧱 基础 25 分钟:核心概念
什么是函数?你可以把函数想象成「自动贩卖机」
投入原料 → 机器内部加工 → 输出产品
你不需要知道里面怎么运转,投钱 + 选按钮 = 拿到东西。
为什么要用函数?
- 避免重复代码写了 100 遍
- 出问题了只改一个地方
- 代码看起来干净利落
\n\n
\n\n
\n\n
第一个函数:Say Hello
def greet(name):
"""打招呼的函数"""
print(f"你好,{name}!欢迎学习 Python!")
# 调用函数
greet("小明")
greet("小红")
你好,小明!欢迎学习 Python!
你好,小红!欢迎学习 Python!
def 就是「定义」的缩写,greet 是函数名,name 是参数(原料),print 是输出(产品)。
函数还能返回值:不只是打印
def add(a, b):
"""计算两个数的和"""
result = a + b
return result
# 用变量接收返回值
total = add(3, 5)
print(f"3 + 5 = {total}")
3 + 5 = 8
return 就是「把东西还给你」,不像 print 只是「显示给你看」。
参数默认值:让函数更灵活
def make_coffee(coffee="美式", size="中杯"):
"""做咖啡,默认美式中杯"""
return f"{size}的{coffee},好了!"
print(make_coffee()) # 用默认参数
print(make_coffee("拿铁")) # 改咖啡类型
print(make_coffee("摩卡", "大杯")) # 全部自定义
中杯的美式,好了!
中杯的拿铁,好了!
大杯的摩卡,好了!
生活类比:点麦当劳,套餐默认薯条可乐,但你非要点麦乐鸡也行。
什么是模块?模块就是一个「工具箱」
Python 自带很多工具箱(模块),你想用哪个就「导入」哪个。
import math
# 用 math 工具箱里的 sqrt(开平方根)
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.141592653589793
4.0
3.141592653589793
为什么要用模块?
- 不用自己造轮子
- 别人测试好的代码,直接用
- 分工明确,各管各的
常用标准模块一览
import random # 随机数
import datetime # 日期时间
import os # 操作系统相关
# 随机抽一个
print(random.choice(["小明", "小红", "小刚"]))
# 今天的日期
print(datetime.date.today())
# 当前目录
print(os.getcwd())
小红
2026-06-26
/Users/apple/workspace
第三方模块:更强大的工具箱
Python 最厉害的地方是有几十万个第三方库!
# 安装:pip install requests
import requests
# 发送网络请求
response = requests.get("https://httpbin.org/get")
print(response.status_code)
print(response.json())
200
{'args': {}, 'headers': {'Accept': '*/*', 'Host': 'httpbin.org', ...}, 'origin': '...', 'url': 'https://httpbin.org/get'}
自己写模块:把函数存起来
创建一个文件 my_tools.py:
# my_tools.py
def calculate_area(width, height):
"""计算矩形面积"""
return width * height
def calculate_perimeter(width, height):
"""计算矩形周长"""
return 2 * (width + height)
在另一个文件里用:
# main.py
from my_tools import calculate_area, calculate_perimeter
w, h = 10, 5
print(f"面积:{calculate_area(w, h)}")
print(f"周长:{calculate_perimeter(w, h)}")
面积:50
周长:30
这就是「组件化」思维:把常用的功能打包,用的时候直接导入。
🔥 实战 35 分钟:3 个递进小项目
项目 1(5 分钟):温度转换器
def celsius_to_fahrenheit(c):
"""摄氏转华氏"""
return c * 9/5 + 32
def fahrenheit_to_celsius(f):
"""华氏转摄氏"""
return (f - 32) * 5/9
# 测试几个温度
temps = [0, 100, 37, -40]
print("摄氏 → 华氏")
for t in temps:
print(f"{t}°C = {celsius_to_fahrenheit(t):.1f}°F")
摄氏 → 华氏
0°C = 32.0°F
100°C = 212.0°F
37°C = 98.6°F
-40°C = -40.0°F
学到了啥:函数封装 + 参数传递 + 循环调用
项目 2(15 分钟):个人账单统计工具
import datetime
def parse_expense(line):
"""解析一行账单,返回 (日期, 类别, 金额)"""
parts = line.strip().split(",")
date = datetime.datetime.strptime(parts[0], "%Y-%m-%d")
category = parts[1]
amount = float(parts[2])
return date, category, amount
def analyze_expenses(expenses):
"""分析账单,返回统计结果"""
total = 0
by_category = {}
for date, category, amount in expenses:
total += amount
by_category[category] = by_category.get(category, 0) + amount
return total, by_category
# 模拟 CSV 数据
csv_data = """2026-06-01,餐饮,45.5
2026-06-02,交通,12.0
2026-06-03,购物,299.0
2026-06-04,餐饮,68.0
2026-06-05,交通,8.5
2026-06-06,餐饮,52.0"""
# 解析所有行
expenses = [parse_expense(line) for line in csv_data.strip().split("\n")]
# 分析
total, by_category = analyze_expenses(expenses)
print(f"=== 6月账单统计 ===")
print(f"总支出:{total:.2f} 元")
print(f"\n分类明细:")
for cat, amount in sorted(by_category.items(), key=lambda x: -x[1]):
print(f" {cat}: {amount:.2f} 元 ({amount/total*100:.1f}%)")
=== 6月账单统计 ===
总支出:485.00 元
分类明细:
餐饮: 165.50 元 (34.1%)
购物: 299.00 元 (61.6%)
交通: 20.50 元 (4.2%)
学到了啥:
- 函数拆分(解析 + 分析)
- 列表推导式快速处理多行数据
- 模块导入(datetime)
项目 3(15 分钟):待办事项小工具
import json
from datetime import datetime
class TodoList:
"""待办清单管理器"""
def __init__(self, filename="todos.json"):
self.filename = filename
self.todos = self._load()
def _load(self):
"""加载文件"""
try:
with open(self.filename, "r", encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
return []
def _save(self):
"""保存文件"""
with open(self.filename, "w", encoding="utf-8") as f:
json.dump(self.todos, f, ensure_ascii=False, indent=2)
def add(self, task, priority="中"):
"""添加待办"""
todo = {
"id": len(self.todos) + 1,
"task": task,
"priority": priority,
"done": False,
"created": datetime.now().strftime("%Y-%m-%d %H:%M")
}
self.todos.append(todo)
self._save()
print(f"✅ 已添加:「{task}」")
def list(self):
"""列出所有待办"""
if not self.todos:
print("📝 清单是空的!")
return
priority_map = {"高": "🔴", "中": "🟡", "低": "🟢"}
print(f"\n📋 当前待办 ({len(self.todos)} 项):")
for todo in self.todos:
status = "✅" if todo["done"] else "⬜"
icon = priority_map.get(todo["priority"], "⚪")
print(f" {icon} [{todo['id']}] {status} {todo['task']}")
def done(self, task_id):
"""标记完成"""
for todo in self.todos:
if todo["id"] == task_id:
todo["done"] = True
self._save()
print(f"🎉 「{todo['task']}」完成!")
return
print(f"❌ 没找到 ID 为 {task_id} 的待办")
# 使用
if __name__ == "__main__":
todo_list = TodoList()
# 添加几个待办
todo_list.add("买水果", priority="高")
todo_list.add("写周报", priority="中")
todo_list.add("整理桌面", priority="低")
# 列出所有
todo_list.list()
# 标记完成
todo_list.done(1)
todo_list.list()
✅ 已添加:「买水果」
✅ 已添加:「写周报」
✅ 已添加:「整理桌面」
📋 当前待办 (3 项):
🔴 [1] ⬜ 买水果
🟡 [2] ⬜ 写周报
🟢 [3] ⬜ 整理桌面
🎉 「买水果」完成!
📋 当前待办 (3 项):
🔴 [1] ✅ 买水果
🟡 [2] ⬜ 写周报
🟢 [3] ⬜ 整理桌面
学到了啥:
- 类(class)的概念:把数据和函数打包
- 文件读写持久化
- __init__ 构造函数
💪 进阶 20 分钟:常见坑 + 调试技巧
坑 1:参数传递的是引用,不是副本
# ❌ 错误示例
def add_item(bad_list):
bad_list.append("新东西") # 修改了原列表!
my_list = ["原始"]
add_item(my_list)
print(my_list) # ['原始', '新东西'] - 原列表被改了!
# ✅ 正确示例
def add_item_fixed(good_list):
new_list = good_list.copy() # 先复制一份
new_list.append("新东西")
return new_list # 返回新列表
my_list = ["原始"]
result = add_item_fixed(my_list)
print(f"原列表:{my_list}") # ['原始']
print(f"新列表:{result}") # ['原始', '新东西']
坑 2:默认参数别用可变对象
# ❌ 错误示例
def bad_function(items=[]): # 列表是 可变 对象!
items.append("item")
return items
print(bad_function()) # ['item']
print(bad_function()) # ['item', 'item'] - 累积了!
print(bad_function()) # ['item', 'item', 'item']
# ✅ 正确示例
def good_function(items=None):
if items is None:
items = [] # 每次创建新列表
items.append("item")
return items
print(good_function()) # ['item']
print(good_function()) # ['item']
print(good_function()) # ['item']
原理:默认参数在函数定义时就创建了,不是每次调用时创建。
坑 3:导入模块时的路径问题
# ❌ 可能报错
import my_module # 如果当前目录没有,Python 找不到
# ✅ 指定路径
import sys
sys.path.append("/path/to/your/module")
import my_module
坑 4:忘了 return 就拿不到结果
# ❌ 错误示例
def calculate(x):
x * 2 # 算完了但没 return!
result = calculate(5)
print(result) # None - 白算了!
# ✅ 正确示例
def calculate_fixed(x):
return x * 2
result = calculate_fixed(5)
print(result) # 10
坑 5:类的方法第一个参数必须是 self
# ❌ 错误示例
class Calculator:
def add(a, b): # 忘了 self!
return a + b
calc = Calculator()
calc.add(1, 2) # 会报错
# ✅ 正确示例
class Calculator:
def add(self, a, b): # self 必须是第一个参数
return a + b
calc = Calculator()
print(calc.add(1, 2)) # 3
调试技巧:print 大法 + pdb
# 简单调试:print 打印中间值
def buggy_function(n):
result = 1
for i in range(n):
result = result * i
print(f"i={i}, result={result}") # 看看每一步的值
return result
# 专业调试:用 pdb
import pdb
def another_function(x):
pdb.set_trace() # 程序会停在这里,等待你输入命令
return x * 2
pdb 常用命令:
- n - 执行下一行
- p 变量名 - 打印变量值
- c - 继续执行直到断点
- q - 退出调试
✏️ 练习题 + 作业题
练习题(5 道,10 分钟)
练习 1(1 分钟):改函数调用
def greet(name, age):
return f"我叫{name},今年{age}岁"
# 修改下面这行,让输出变成 "我叫小红,今年18岁"
result = ???
print(result)
- 预期输出:
我叫小红,今年18岁 - 提示:直接传入两个参数
练习 2(2 分钟):加个判断
def check_age(age):
# 当 age >= 18 时返回 "成年人",否则返回 "未成年"
???
return ???
print(check_age(20))
print(check_age(16))
- 预期输出:
成年人和未成年 - 提示:用 if 判断 age 和 18 的大小
练习 3(2 分钟):处理新数据
用项目 2 的 parse_expense 函数解析这行数据:
line = "2026-06-15,娱乐,199.9"
# 调用 parse_expense 解析它,打印出日期、类别、金额
- 预期输出:日期对象、类别字符串、金额浮点数
- 提示:函数返回的是元组,用多个变量接收
练习 4(3 分钟):改造 TodoList
给 TodoList 类添加一个 delete 方法,删除指定 ID 的待办:
# 参考 done 方法的实现
def delete(self, task_id):
???
- 预期输出:删除后列表里不再有这个待办
- 提示:遍历列表,找 ID,移除
练习 5(2 分钟):看报错猜原因
def process(data):
return data.append("new")
my_data = ["a", "b"]
result = process(my_data)
print(result)
- 预期输出:
None - 提示:
append返回什么?
作业题(30 分钟-2 小时)
作业:做一个「个人账本工具」
- 需求描述:做一个命令行账本,能记账、查账、统计
- 功能点:
1. 添加一笔支出(日期、类别、金额、备注)
2. 查看所有支出列表
3. 按月份统计总支出
4. 数据保存到本地文件,程序重启不丢失 - 加分项:
1. 支持收入记账(和支出分开统计)
2. 按类别筛选查看 - 验收标准:
- 能运行不报错
- 添加后重启程序数据还在
- 统计结果正确
- 代码有注释说明
- 提交方式:评论区贴代码或 GitHub 链接
📚 总结 + 资源
一句话总结:函数是代码的「积木块」,模块是函数的「工具箱」,学会分工协作,代码才能又长又稳。
延伸学习:
- 官方文档:https://docs.python.org/zh-cn/3/tutorial/
- 《Python编程:从入门到实践》- 第 3-6 章
- 视频:B 站小甲鱼 Python 教程(第 15-20 集)
互动钩子:你在写代码时有没有「一个人写了几百行,改一处坏三处」的惨痛经历?评论区聊聊,老粉优先回复!
下章剧透:学会了函数和模块,下一章我们要让「不同的零件」能互相传递消息——组件之间怎么通信?数据怎么从父组件传到子组件? 敬请期待!

评论(0)