Python 学习笔记(二):函数进阶与模块化
前言
上一篇记录了 Python 基础语法(数据类型、循环、字符串、文件读写),这次进入第二阶段:函数进阶与模块化。
这阶段最大的感受是:从”写脚本”进化到”写程序”。之前 BMI 计算器是自上而下一把梭,现在开始学会拆函数、拆模块、处理异常——代码从”能跑”变成”能维护”。
一、函数基础
1.1 定义与调用
1 | def calculate_bmi(weight: float, height: float) -> float: |
Python 函数相比 JS 的几个不同点:
多返回值(自动打包成 tuple):
1 | def get_circle_info(radius: float) -> tuple[float, float]: |
关键字参数(可读性极高):
1 | def describe_pet(animal: str, name: str, age: int = 1) -> None: |
1.2 作用域 — LEGB 规则
Python 查找变量的顺序:Local → Enclosing → Global → Built-in
1 | def demo() -> None: |
global 关键字:在函数里修改全局变量,能不用就不用——改成参数传递和返回值更干净。
二、进阶参数 — 这阶段的重头戏
2.1 *args:接收任意多个位置参数
1 | def add_all(*numbers: int) -> int: |
对比 JS:相当于 function add(...numbers) { } 的 rest 参数,思路完全一样。
2.2 **kwargs:接收任意多个关键字参数
1 | def create_user(name: str, age: int, **kwargs: object) -> dict: |
2.3 解包运算符 * / **
调用函数时把列表/字典”展开”传入:
1 | def introduce(name: str, age: int, city: str) -> str: |
2.4 ⚠️ 默认参数陷阱 — 新手必踩
这是 Python 最著名的坑之一,不要用可变对象(list/dict)做默认参数:
1 | # ❌ 错误:默认参数在函数定义时只创建一次,所有调用共享同一个对象! |
原因:Python 的默认参数在 def 执行时只求值一次,之后所有调用共享同一个列表对象。
三、模块化
3.1 import 的几种方式
1 | import game_utils # 方式1:导入整个模块 |
最佳实践:
- 只 import 需要的名字,避免命名空间污染
- 模块自己也有
__name__属性:被导入时是模块名,直接运行时是"__main__"
3.2 __name__ == "__main__" — 模块的双重身份
这个惯用法让同一个文件既可以被导入,又可以独立运行:
1 | # game_utils.py |
对比 JS:Node 里也有类似概念(require.main === module),但 Python 的这个写法更常见,几乎每个文件结尾都能看到。
3.3 实战:模块拆分
把猜数字游戏拆成两个文件,职责分明:
1 | 02_functions/ |
工具层不关心游戏怎么玩,只管”给我数字,我返回提示”;流程层不关心数字怎么生成,只管调用。这就是模块化的核心思想。
四、异常处理
4.1 完整结构
1 | try: |
记忆口诀:try 试 → except 抓 → else 成了 → finally 收尾。
4.2 raise 主动抛出 + 自定义异常
1 | class InsufficientFundsError(Exception): |
4.3 实战:健壮的用户输入
学异常处理之前,猜数字游戏只要用户输入字母就崩溃。学完之后的解决方案:
1 | def get_valid_int(prompt: str, min_val: int, max_val: int) -> int: |
核心思想:把”可能会错”的代码放进 try,错了就优雅提示并循环重试,绝不崩溃。
五、综合项目:命令行计算器
把函数、模块、异常处理全部整合成一个完整程序:
5.1 架构设计
1 | calculator_ops.py # 运算层:add/subtract/multiply/divide |
5.2 亮点一:*args 支持任意多个数字
1 | def add(*numbers: float) -> float: |
5.3 亮点二:字典映射代替 if/elif
1 | op_map = { |
比一长串 if choice == "1": ... elif choice == "2": ... 简洁得多,也容易扩展。
5.4 踩过的两个坑
坑1:循环里漏了 return
1 | def divide(*numbers): |
坑2:split(' ') vs split()
1 | "1 2 3".split(' ') # ['1', '', '2', '', '3'] ← 多个空格产生空字符串! |
split() 不带参数会同时处理空格、制表符、换行,且连续空白只算一个,永远比 split(' ') 更安全。
学习心得
1. 从”写脚本”到”写程序”
阶段1的 BMI 计算器是自上而下一把梭,能跑就行。这阶段学会了拆函数、拆模块、处理异常——同样的功能,代码从”能跑”变成了”能维护”。
2. 默认参数陷阱是最值得记的一个坑
def f(items=[]) 看起来人畜无害,实际上所有调用共享同一个列表。这不像 JS 每次调用都新建。养成”默认参数用 None”的习惯。
3. 异常处理不是”出问题才写”
get_valid_int 这个函数改变了我的思维:输入验证不是”出了问题再补”,而是设计时就考虑用户会乱输。把”可能会错”的代码放进 try,是健壮程序的标配。
4. 模块化让代码有了结构
game_utils.py 和主程序分开后,工具函数可以被多个程序复用,主程序的逻辑也更清晰。这种”纯函数 + 流程编排”的分层,和前端项目里 utils/ + views/ 的思路一脉相承。
项目地址:github.com/halely/python-study
下一篇预告:Python 面向对象——类、继承、魔术方法。
学习时间:2026-06-17



