模块、包与异常处理
学到这里,你已经会写越来越多的 Python 代码了。接下来会遇到三个非常现实的问题:
- 代码多起来以后,怎么拆分文件
- 别人写好的功能,怎么导入进来直接用
- 程序运行出错时,怎么优雅处理而不是直接崩掉
这三个问题,看起来分别属于不同方向,但实际上它们都和“让程序更像一个真正项目”有关。
所以这一节的重点,不只是会写几行 import 或 try...except,而是开始建立工程化思维。
本节目标
- 理解什么是模块和包
- 学会使用
import导入模块 - 理解标准库和自定义模块
- 学会使用几个高频标准库
- 理解什么是异常
- 学会使用
try...except处理错误 - 知道什么时候应该处理异常,什么时候应该先排查原因
为什么代码需要拆成模块
当你刚开始学习时,所有代码写在一个文件里问题不大。
例如:
- 一个练习题
- 一个几十行的小脚本
- 一个简单计算器
但随着功能变多,你会很快发现:
- 一个文件越来越长
- 不同功能混在一起
- 找代码越来越慢
- 修改某一部分时容易影响其他部分
这时就需要“模块化”。
模块化的核心思想很简单:
- 一个文件负责一类功能
- 不同功能拆到不同文件里
- 需要的时候再导入使用
模块是什么
在 Python 里,一个 .py 文件通常就可以看成一个模块。
例如你有一个文件叫 tools.py:
def add(a, b):
return a + b
def sub(a, b):
return a - b那么 tools.py 就是一个模块。
它的意义是:把相关功能集中放在一起,方便复用。
如何导入模块
最基础的 import
import math
print(math.sqrt(16))输出结果:
4.0这里的意思是:
- 导入
math模块 - 通过
math.sqrt()调用里面的平方根函数
这种写法很清晰,因为你一眼就能知道 sqrt 是来自 math 模块。
from ... import ...
from math import sqrt
print(sqrt(25))输出结果:
5.0这种写法更短,但也要注意:如果导入太多名字,代码可读性可能会下降。
起别名
import datetime as dt
print(dt.datetime.now().year)输出结果示例:
2026给模块起别名常用于:
- 模块名很长
- 代码中会频繁使用
- 行业内已经有常见缩写习惯
自定义模块
标准库是别人帮你准备好的模块,但你自己也可以写模块。
例如新建一个 tools.py:
def add(a, b):
return a + b
def say_hello(name):
return f"你好,{name}"然后在另一个文件里使用:
import tools
print(tools.add(10, 20))
print(tools.say_hello("小明"))输出结果:
30
你好,小明这就是最基础的“自己写模块,再导入使用”。
包是什么
如果模块再多一些,只靠一个目录已经不好管理了,这时就会出现包。
你可以先把包理解成:用来组织多个模块的目录。
例如:
utils/
math_tools.py
string_tools.py
file_tools.py这里的 utils 就可以理解成一个包结构。
零基础阶段你不需要一开始就深挖包的各种细节,但至少要先建立这个认知:
- 模块是单个功能文件
- 包是模块的组织方式
标准库是什么
标准库就是 Python 安装后自带的一批常用模块。
优点是:
- 不需要额外安装
- 直接导入就能使用
- 覆盖大量常见场景
你后面最常接触的标准库包括:
jsontimedatetimeloggingbase64osre
下面我们挑几个高频模块做最基础说明。
json:处理 JSON 数据
import json
data = {"name": "小明", "age": 18}
text = json.dumps(data, ensure_ascii=False)
print(text)
print(type(text))输出结果:
{"name": "小明", "age": 18}
<class 'str'>作用:
- Python 对象转 JSON 字符串
- 接口数据处理
- 配置文件保存
time:时间戳和暂停
import time
print(time.time())
time.sleep(1)
print("1 秒后继续")输出结果示例:
1713945600.123456
1 秒后继续作用:
- 统计耗时
- 请求间隔
- 时间戳处理
datetime:日期时间对象
from datetime import datetime
now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))输出结果示例:
2026-04-24 12:30:00作用:
- 时间格式化
- 时间计算
- 日志时间记录
logging:日志
import logging
logging.basicConfig(level=logging.INFO)
logging.info("程序启动成功")
logging.error("发生了一个模拟错误")输出结果示例:
INFO:root:程序启动成功
ERROR:root:发生了一个模拟错误作用:
- 记录程序运行过程
- 记录警告和错误
- 比
print()更适合真实项目
程序报错时到底发生了什么
现在开始看异常处理。
先看一个最简单的例子:
num = int("abc")
print(num)运行后会报错,因为 "abc" 不能转换成整数。
这种程序运行过程中发生的错误,在 Python 里通常叫异常。
什么是异常
异常可以简单理解成:程序在执行时遇到了不正常情况,导致正常流程被打断。
常见异常包括:
ValueError:值不合法TypeError:类型不对NameError:变量未定义IndexError:下标越界KeyError:字典键不存在FileNotFoundError:文件不存在
为什么要处理异常
因为真实程序里,不可能保证每一步都百分百成功。
例如:
- 用户输入了错误内容
- 文件路径写错了
- 数据库连接失败了
- 接口返回了异常数据
如果你完全不处理异常,程序就会在出错时直接中断。
最基础的 try...except
try:
num = int("abc")
print(num)
except ValueError:
print("转换失败,输入内容不是整数")输出结果:
转换失败,输入内容不是整数这里的逻辑是:
try:先尝试执行可能出错的代码except:如果出现指定异常,就执行这里的处理逻辑
一个更贴近真实场景的例子
text = input("请输入一个整数:")
try:
num = int(text)
print(f"你输入的整数是:{num}")
except ValueError:
print("输入错误,请输入纯数字")如果用户输入:
123输出结果:
你输入的整数是:123如果用户输入:
abc输出结果:
输入错误,请输入纯数字这就比程序直接崩掉友好得多。
捕获多个异常
try:
data = {"name": "小明"}
print(data["age"])
except KeyError:
print("字典里没有这个键")输出结果:
字典里没有这个键也可以写多个异常分支:
try:
num = int(input("请输入整数:"))
print(10 / num)
except ValueError:
print("输入的不是整数")
except ZeroDivisionError:
print("除数不能为 0")这说明不同错误可以分别处理。
else 和 finally
异常处理中还有两个常见结构。
else
如果 try 里的代码没有报错,就会执行 else。
try:
num = int("12")
except ValueError:
print("转换失败")
else:
print("转换成功")
print(num)输出结果:
转换成功
12finally
无论有没有异常,finally 里的代码都会执行。
try:
print("开始处理")
num = int("12")
except ValueError:
print("发生异常")
finally:
print("程序结束前一定会执行这里")输出结果:
开始处理
程序结束前一定会执行这里它常用于:
- 资源清理
- 文件关闭
- 数据库连接释放
什么时候该处理异常,什么时候该先排错
这是一个非常重要的意识。
不是所有异常都应该被“吞掉”。
应该处理的情况
- 用户输入错误
- 文件可能不存在
- 网络请求可能失败
- 第三方接口返回不稳定
应该优先排错的情况
- 自己写错了变量名
- 逻辑明显有 bug
- 路径本来就应该存在却不存在
也就是说:
- 异常处理是为了应对“合理范围内可能发生的问题”
- 不是为了掩盖程序本身的错误
一个综合小例子:读取文件中的数字并求和
假设文件 numbers.txt 内容如下:
10
20
abc
30代码如下:
total = 0
with open("numbers.txt", "r", encoding="utf-8") as file:
for line in file:
text = line.strip()
try:
num = int(text)
total += num
except ValueError:
print(f"跳过无效数据:{text}")
print(f"最终总和:{total}")输出结果:
跳过无效数据:abc
最终总和:60这个例子很接近真实项目场景:
- 数据不一定干净
- 程序不能因为一条坏数据就直接崩掉
- 你需要既继续执行,又知道哪里出了问题
常见错误
1. 模块名和文件名冲突
例如你自己创建了一个文件叫 json.py,然后又写:
import json这时很可能导入的是你自己的文件,而不是标准库里的 json 模块。
2. 乱用 from xxx import *
这种写法会把很多名字直接导入当前作用域,容易造成命名混乱。零基础阶段尽量少用。
3. except 写得过于宽泛
例如:
except Exception:
pass这种写法会把错误直接吞掉,调试时非常痛苦。初学阶段更推荐明确捕获具体异常。
4. 遇到报错只想着“怎么消灭报错”
正确思路应该是:
- 看清异常类型
- 看清报错位置
- 判断这是预期问题还是代码 bug
- 再决定要不要用异常处理
练习题
练习 1
自己创建一个 tools.py 模块,里面写两个函数:
add(a, b)mul(a, b)
再在另一个文件里导入并调用它们。
练习 2
使用 json 把一个字典转换成 JSON 字符串,再转换回来。
练习 3
写一段程序,让用户输入整数;如果输入错误,就提示“请输入正确的整数”。
练习 4
读取一个文件内容;如果文件不存在,就捕获异常并输出“文件不存在”。
练习 5
写一个循环,遍历一个列表,把里面的字符串逐个转成整数;如果某个元素无法转换,就跳过它。
本节小结
这一节的核心,不只是学会几个语法点,而是开始建立项目组织意识。
你应该记住:
- 模块让代码拆分更清晰
- 包让多个模块更有组织
- 标准库让你不用重复造轮子
- 异常处理让程序在面对问题时更稳
后面你写 Django、爬虫、数据库脚本时,这些能力都会反复出现。它们看起来不像循环和函数那样“显眼”,但在真实开发里非常重要。