Python 模块、包与异常处理:构建更稳健的程序
当代码量增长时,把所有逻辑堆在一个文件里会变得难以维护。Python 通过模块 和包 来组织代码,实现复用与结构清晰;而异常处理则让程序在遇到错误时能够优雅应对,而不是直接崩溃。本文系统梳理这三项核心机制。
一、模块
1. 什么是模块
模块就是一个以 .py 结尾的 Python 文件,文件名即为模块名。模块是 Python 组织代码的基本单位,可以将变量、函数、类等定义在一个文件中,供其他程序导入使用。
模块主要分三类:
- 内置模块 :Python 自带的,如
sys、os、time - 第三方模块 :通过 pip 安装的,如
requests、loguru - 自定义模块 :开发者自己编写的
.py文件
2. 模块的导入方式
| 导入语法 | 使用方式 |
|---|---|
import 模块名 |
模块名.函数() |
import 模块名 as 别名 |
别名.函数() |
from 模块名 import 名称 |
直接使用 名称() |
from 模块名 import 名称 as 别名 |
别名() |
from 模块名 import * |
导入所有(不推荐,易造成命名污染) |
python
import math
print(math.sqrt(16)) # 4.0
from datetime import datetime
print(datetime.now()) # 当前时间
3. 模块的搜索路径
当执行 import 时,Python 会按照 sys.path 中列出的路径依次查找目标模块。sys.path 默认包含当前目录、标准库目录和第三方包安装目录等。
python
import sys
print(sys.path)
4. __name__ 特殊属性
每个模块都有一个内置属性 __name__:
- 当模块被直接运行 时,
__name__的值为"__main__"。 - 当模块被导入 时,
__name__的值为模块本身的文件名。
这常用于编写只在直接运行时才执行的测试代码:
python
if __name__ == "__main__":
print("这段代码只在直接运行时执行")
二、包
1. 什么是包
包是包含多个模块的目录 ,用于更大型项目的层级化代码组织。包与普通目录的区别在于它必须包含一个 __init__.py 文件(Python 3.3+ 可省略,但建议保留以明确标识)。
包的目录结构示例:
my_package/
├── __init__.py
├── module_a.py
├── module_b.py
└── sub_pkg/
├── __init__.py
└── module_c.py
包的核心作用:
- 组织代码,避免模块间命名冲突
- 提供层次化结构,便于管理大型项目
- 通过
__init__.py控制 API 暴露范围
2. __init__.py 的作用
- 标识包:告诉 Python 该目录是一个包。
- 初始化代码:导入包时自动执行,可放置包级初始化逻辑。
- 控制暴露接口 :定义
__all__列表,限制from package import *时哪些模块会被导入。 - 简化导入:可在其中预先导入常用模块,让使用者更便捷地访问深层功能。
python
# __init__.py 示例
__all__ = ["module_a", "module_b"] # 只暴露这两个模块
from .module_a import core_function # 提前导入,简化调用
未列入
__all__的内部实现模块,仍可通过完整路径(如my_package.internal_module)访问。
3. 包的导入方式
绝对导入
python
import 包名.模块名
from 包名 import 模块名
from 包名.模块名 import 函数名
import 包名.子包.模块名 as 别名
示例:
python
import my_package.module_a
from my_package import module_b
from my_package.sub_pkg.module_c import some_func
import my_package.sub_pkg.module_c as mc
相对导入(在包内部使用)
python
from . import 同级模块 # 导入当前包的其它模块
from .module_a import func # 从同级模块导入函数
from .. import 上级模块 # 导入上级包的模块
相对导入仅在包内部有效,直接运行包含相对导入的模块会报错。
4. 包的搜索路径
与模块一致,包的查找同样依赖 sys.path。只要包的根目录在 sys.path 中,就能通过绝对导入找到。
5. 命名规范
- 包名使用全小写字母 + 下划线组合,简洁且描述性强
- 避免与 Python 标准库名称冲突
三、异常处理
程序运行时难免遇到错误。Python 将错误分为两类:
- 语法错误:编译阶段发现,如漏写括号、缩进错误。
- 异常 :运行时发生的错误,如
a = 1 / 0会抛出ZeroDivisionError。
异常可以通过明确的处理机制来捕获和响应,防止程序直接中断。
1. try-except 结构
最基本的异常捕获结构:
python
try:
# 可能出错的代码
result = 1 / 0
except ZeroDivisionError:
# 捕获特定异常后执行的代码
print("除数不能为零")
2. 完整语法:try-except-else-finally
python
try:
# 尝试运行的代码
num = int(input("请输入数字: "))
result = 10 / num
except ValueError:
# 捕获值错误
print("输入的不是有效数字")
except ZeroDivisionError:
# 捕获除零错误
print("除数不能为零")
else:
# 无异常时执行
print(f"计算结果: {result}")
finally:
# 无论是否异常都会执行
print("程序结束")
各子句职责:
try:包裹可能异常的代码except:捕获对应异常类型并处理else:仅当 try 中无任何异常时执行finally:无论有无异常都会执行,常用作资源释放(如关闭文件、数据库连接)
3. 异常的捕获规则
- 异常会由层级最近 的匹配
except捕获 - 匹配成功后,该
try块内后续代码不再执行 - 未被捕获的异常会向外层传播,最终导致程序终止
python
try:
try:
1 / 0
except ValueError: # 不匹配 ZeroDivisionError
print("内层未捕获")
# 这里不会被执行
except ZeroDivisionError: # 外层成功捕获
print("外层捕获了除零错误")
4. 主动抛出异常:raise
使用 raise 可以在代码中主动触发异常,常用于业务逻辑中的输入校验或状态检测:
python
def set_age(age):
if age < 0:
raise ValueError("年龄不能为负数")
print(f"年龄设置为: {age}")
set_age(-5) # 抛出 ValueError: 年龄不能为负数
语法:
python
raise 异常类型("错误信息描述")
小结
本文围绕 Python 项目组织与程序健壮性,梳理了三大主题:
- 模块 :单个
.py文件,提供多种导入方式与__name__机制。 - 包 :多模块的层级目录结构,通过
__init__.py控制接口暴露,支持绝对导入与相对导入。 - 异常处理 :用
try/except/else/finally捕获运行时错误,利用raise主动抛出异常,让程序在异常情况下也能有序应对。
掌握模块和包,能帮你写出结构清晰、可复用的大型项目;而扎实的异常处理能力,则让你的代码在生产环境中更加稳健可靠。