Skip to content

模块、包与异常处理

学到这里,你已经会写越来越多的 Python 代码了。接下来会遇到三个非常现实的问题:

  1. 代码多起来以后,怎么拆分文件
  2. 别人写好的功能,怎么导入进来直接用
  3. 程序运行出错时,怎么优雅处理而不是直接崩掉

这三个问题,看起来分别属于不同方向,但实际上它们都和“让程序更像一个真正项目”有关。

所以这一节的重点,不只是会写几行 importtry...except,而是开始建立工程化思维。

本节目标

  • 理解什么是模块和包
  • 学会使用 import 导入模块
  • 理解标准库和自定义模块
  • 学会使用几个高频标准库
  • 理解什么是异常
  • 学会使用 try...except 处理错误
  • 知道什么时候应该处理异常,什么时候应该先排查原因

为什么代码需要拆成模块

当你刚开始学习时,所有代码写在一个文件里问题不大。

例如:

  • 一个练习题
  • 一个几十行的小脚本
  • 一个简单计算器

但随着功能变多,你会很快发现:

  • 一个文件越来越长
  • 不同功能混在一起
  • 找代码越来越慢
  • 修改某一部分时容易影响其他部分

这时就需要“模块化”。

模块化的核心思想很简单:

  • 一个文件负责一类功能
  • 不同功能拆到不同文件里
  • 需要的时候再导入使用

模块是什么

在 Python 里,一个 .py 文件通常就可以看成一个模块。

例如你有一个文件叫 tools.py

python
def add(a, b):
    return a + b


def sub(a, b):
    return a - b

那么 tools.py 就是一个模块。

它的意义是:把相关功能集中放在一起,方便复用。

如何导入模块

最基础的 import

python
import math

print(math.sqrt(16))

输出结果:

text
4.0

这里的意思是:

  • 导入 math 模块
  • 通过 math.sqrt() 调用里面的平方根函数

这种写法很清晰,因为你一眼就能知道 sqrt 是来自 math 模块。

from ... import ...

python
from math import sqrt

print(sqrt(25))

输出结果:

text
5.0

这种写法更短,但也要注意:如果导入太多名字,代码可读性可能会下降。

起别名

python
import datetime as dt

print(dt.datetime.now().year)

输出结果示例:

text
2026

给模块起别名常用于:

  • 模块名很长
  • 代码中会频繁使用
  • 行业内已经有常见缩写习惯

自定义模块

标准库是别人帮你准备好的模块,但你自己也可以写模块。

例如新建一个 tools.py

python
def add(a, b):
    return a + b


def say_hello(name):
    return f"你好,{name}"

然后在另一个文件里使用:

python
import tools

print(tools.add(10, 20))
print(tools.say_hello("小明"))

输出结果:

text
30
你好,小明

这就是最基础的“自己写模块,再导入使用”。

包是什么

如果模块再多一些,只靠一个目录已经不好管理了,这时就会出现包。

你可以先把包理解成:用来组织多个模块的目录。

例如:

text
utils/
  math_tools.py
  string_tools.py
  file_tools.py

这里的 utils 就可以理解成一个包结构。

零基础阶段你不需要一开始就深挖包的各种细节,但至少要先建立这个认知:

  • 模块是单个功能文件
  • 包是模块的组织方式

标准库是什么

标准库就是 Python 安装后自带的一批常用模块。

优点是:

  • 不需要额外安装
  • 直接导入就能使用
  • 覆盖大量常见场景

你后面最常接触的标准库包括:

  • json
  • time
  • datetime
  • logging
  • base64
  • os
  • re

下面我们挑几个高频模块做最基础说明。

json:处理 JSON 数据

python
import json

data = {"name": "小明", "age": 18}
text = json.dumps(data, ensure_ascii=False)

print(text)
print(type(text))

输出结果:

text
{"name": "小明", "age": 18}
<class 'str'>

作用:

  • Python 对象转 JSON 字符串
  • 接口数据处理
  • 配置文件保存

time:时间戳和暂停

python
import time

print(time.time())
time.sleep(1)
print("1 秒后继续")

输出结果示例:

text
1713945600.123456
1 秒后继续

作用:

  • 统计耗时
  • 请求间隔
  • 时间戳处理

datetime:日期时间对象

python
from datetime import datetime

now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))

输出结果示例:

text
2026-04-24 12:30:00

作用:

  • 时间格式化
  • 时间计算
  • 日志时间记录

logging:日志

python
import logging

logging.basicConfig(level=logging.INFO)
logging.info("程序启动成功")
logging.error("发生了一个模拟错误")

输出结果示例:

text
INFO:root:程序启动成功
ERROR:root:发生了一个模拟错误

作用:

  • 记录程序运行过程
  • 记录警告和错误
  • print() 更适合真实项目

程序报错时到底发生了什么

现在开始看异常处理。

先看一个最简单的例子:

python
num = int("abc")
print(num)

运行后会报错,因为 "abc" 不能转换成整数。

这种程序运行过程中发生的错误,在 Python 里通常叫异常。

什么是异常

异常可以简单理解成:程序在执行时遇到了不正常情况,导致正常流程被打断。

常见异常包括:

  • ValueError:值不合法
  • TypeError:类型不对
  • NameError:变量未定义
  • IndexError:下标越界
  • KeyError:字典键不存在
  • FileNotFoundError:文件不存在

为什么要处理异常

因为真实程序里,不可能保证每一步都百分百成功。

例如:

  • 用户输入了错误内容
  • 文件路径写错了
  • 数据库连接失败了
  • 接口返回了异常数据

如果你完全不处理异常,程序就会在出错时直接中断。

最基础的 try...except

python
try:
    num = int("abc")
    print(num)
except ValueError:
    print("转换失败,输入内容不是整数")

输出结果:

text
转换失败,输入内容不是整数

这里的逻辑是:

  • try:先尝试执行可能出错的代码
  • except:如果出现指定异常,就执行这里的处理逻辑

一个更贴近真实场景的例子

python
text = input("请输入一个整数:")

try:
    num = int(text)
    print(f"你输入的整数是:{num}")
except ValueError:
    print("输入错误,请输入纯数字")

如果用户输入:

text
123

输出结果:

text
你输入的整数是:123

如果用户输入:

text
abc

输出结果:

text
输入错误,请输入纯数字

这就比程序直接崩掉友好得多。

捕获多个异常

python
try:
    data = {"name": "小明"}
    print(data["age"])
except KeyError:
    print("字典里没有这个键")

输出结果:

text
字典里没有这个键

也可以写多个异常分支:

python
try:
    num = int(input("请输入整数:"))
    print(10 / num)
except ValueError:
    print("输入的不是整数")
except ZeroDivisionError:
    print("除数不能为 0")

这说明不同错误可以分别处理。

elsefinally

异常处理中还有两个常见结构。

else

如果 try 里的代码没有报错,就会执行 else

python
try:
    num = int("12")
except ValueError:
    print("转换失败")
else:
    print("转换成功")
    print(num)

输出结果:

text
转换成功
12

finally

无论有没有异常,finally 里的代码都会执行。

python
try:
    print("开始处理")
    num = int("12")
except ValueError:
    print("发生异常")
finally:
    print("程序结束前一定会执行这里")

输出结果:

text
开始处理
程序结束前一定会执行这里

它常用于:

  • 资源清理
  • 文件关闭
  • 数据库连接释放

什么时候该处理异常,什么时候该先排错

这是一个非常重要的意识。

不是所有异常都应该被“吞掉”。

应该处理的情况

  • 用户输入错误
  • 文件可能不存在
  • 网络请求可能失败
  • 第三方接口返回不稳定

应该优先排错的情况

  • 自己写错了变量名
  • 逻辑明显有 bug
  • 路径本来就应该存在却不存在

也就是说:

  • 异常处理是为了应对“合理范围内可能发生的问题”
  • 不是为了掩盖程序本身的错误

一个综合小例子:读取文件中的数字并求和

假设文件 numbers.txt 内容如下:

text
10
20
abc
30

代码如下:

python
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}")

输出结果:

text
跳过无效数据:abc
最终总和:60

这个例子很接近真实项目场景:

  • 数据不一定干净
  • 程序不能因为一条坏数据就直接崩掉
  • 你需要既继续执行,又知道哪里出了问题

常见错误

1. 模块名和文件名冲突

例如你自己创建了一个文件叫 json.py,然后又写:

python
import json

这时很可能导入的是你自己的文件,而不是标准库里的 json 模块。

2. 乱用 from xxx import *

这种写法会把很多名字直接导入当前作用域,容易造成命名混乱。零基础阶段尽量少用。

3. except 写得过于宽泛

例如:

python
except Exception:
    pass

这种写法会把错误直接吞掉,调试时非常痛苦。初学阶段更推荐明确捕获具体异常。

4. 遇到报错只想着“怎么消灭报错”

正确思路应该是:

  1. 看清异常类型
  2. 看清报错位置
  3. 判断这是预期问题还是代码 bug
  4. 再决定要不要用异常处理

练习题

练习 1

自己创建一个 tools.py 模块,里面写两个函数:

  • add(a, b)
  • mul(a, b)

再在另一个文件里导入并调用它们。

练习 2

使用 json 把一个字典转换成 JSON 字符串,再转换回来。

练习 3

写一段程序,让用户输入整数;如果输入错误,就提示“请输入正确的整数”。

练习 4

读取一个文件内容;如果文件不存在,就捕获异常并输出“文件不存在”。

练习 5

写一个循环,遍历一个列表,把里面的字符串逐个转成整数;如果某个元素无法转换,就跳过它。

本节小结

这一节的核心,不只是学会几个语法点,而是开始建立项目组织意识。

你应该记住:

  • 模块让代码拆分更清晰
  • 包让多个模块更有组织
  • 标准库让你不用重复造轮子
  • 异常处理让程序在面对问题时更稳

后面你写 Django、爬虫、数据库脚本时,这些能力都会反复出现。它们看起来不像循环和函数那样“显眼”,但在真实开发里非常重要。

Built with VitePress.