Python模块与包教程:从「乱堆代码」到「结构专家」
刚入门Python时,我们总喜欢把所有函数、变量、逻辑一股脑塞进 main.py——一开始写个爬虫、计算器还挺爽,但当代码涨到几百上千行,或者想复用以前写过的某段工具函数时,问题就来了:变量名冲突(比如两个功能里都有叫 get_total 的函数)、函数翻半天找不到、代码根本不敢删不敢改,怕牵一发动全身。
这时候,模块(Module)和包(Package)就是你的救星!它们能帮你把代码拆成逻辑清晰的小单元,既能独立复用,又能避免命名打架。
1. 模块:你的第一个「代码收纳盒」
什么是模块?
简单来说,模块就是一个 .py 文件,文件名就是它的模块名(比如 math_utils.py 的模块名是 math_utils)。
它可以装:
- 函数定义
- 变量定义
- 类定义
- 一些执行语句(但不建议写太多主逻辑,除非模块是用来直接运行的)
模块的核心优势
把原来的大文件拆成模块,能带来这4个实打实的好处:
- 像整理桌面一样好维护:把“数学计算”“文件操作”“数据格式化”这类功能分开,下次改只动对应的小文件,不会影响其他逻辑
- 不用重复造轮子:写好的工具模块可以复制到任何项目,或者上传到PyPI让别人用
- 彻底解决命名冲突:不同模块可以有同名函数/变量,调用时加个前缀就行(比如
math_utils.get_totalvssales_utils.get_total) - 悄悄提升加载速度:Python第一次导入模块后,会把编译好的字节码存成
.pyc文件(放在__pycache__目录),下次直接读缓存,不用再解析.py源码
2. 包:模块的「多层收纳架」
当你的模块越来越多,比如有10个工具模块、5个数据模型模块,光放一层目录又乱了——这时候就需要包,用目录结构来分类管理模块!
什么是包?
包本质就是一个目录,但(为了兼容性)建议在里面放一个空的或有内容的 __init__.py 文件,用来告诉Python:“嘿,这是个Python包,不是普通文件夹!”
一个标准的项目包结构
给大家举个电商后台小项目的结构,一目了然:
别小看 __init__.py!
虽然Python 3.3+(也就是命名空间包)允许空包不用 __init__.py,但建议所有项目都保留,它能干这3件大事:
- 标识身份:兼容旧版本的Python和部分IDE工具
- 简化导入:把常用的模块/函数直接暴露在包的顶层,不用写长长的子包路径
- 控制公开范围:用
__all__列表定义from package import *时会导入哪些内容,防止把内部辅助函数也导出来污染命名空间
后面的示例里会给大家演示具体用法。
3. 模块/包的4种常用导入方式
3.1 基本导入(最推荐的绝对导入)
绝对导入的路径最清晰,不管代码放在哪个位置运行都不会出错,是官方推荐的方式:
3.2 相对导入(只在包内部用!)
相对导入用 .(当前目录)和 ..(上级目录)来简化同项目内的路径,但不能在包外部的入口文件里用,否则会报错 ValueError: attempted relative import with no known parent package。
比如在 ecommerce_backend/models/user.py 里,要导入同级的 order.py,或者上级的 utils 包:
4. Python是怎么找到你的模块/包的?
有时候导入会报 ModuleNotFoundError,这不是模块丢了,是Python没找到它——先搞懂Python的搜索路径顺序,解决问题就快多了:
- 内置模块:比如
ossysmath,这些是Python自带的,优先级最高 - 当前运行脚本所在的目录(如果是交互式运行,就是当前终端打开的目录)
- PYTHONPATH环境变量指定的目录(我们可以手动把常用的项目路径加进去)
- 依赖安装的默认路径:比如
site-packages(用pip install装的第三方库都在这)
快速查看当前的搜索路径
5. 新手避坑的5个最佳实践
5.1 模块/包命名要规范
- 模块名用全小写+下划线,比如
user_service.pydata_parser.py,别用驼峰(UserService.py)或者大写 - 绝对不能和Python标准库/常用第三方库重名!比如别叫
os.pyrequests.py,否则会覆盖掉官方库,报一堆奇怪的错 - 测试重名很简单:在终端打开Python,输入
import 你想取的名字,如果没报错,说明已经被占了,赶紧换
5.2 别用 from module import *
这种导入会把模块里的所有内容(包括内部辅助函数)都导到当前命名空间,不仅容易冲突,还不知道函数到底是从哪来的——除非你在模块/包的 __init__.py 里用 __all__ 明确规定了范围。
5.3 导入语句要放对位置
- 统一放在文件最顶部
- 按“标准库→第三方库→本地模块/包”分组导入,每组之间空一行,看起来更整齐
- 可以加单行注释说明每组的用途
比如:
5.4 用 if __name__ == '__main__' 做自测试
每个模块写完后,可以加这段代码来测试功能,这样只有直接运行这个模块时才会执行测试,导入模块时不会:
5.5 包结构要“扁平化”
尽量别搞超过3层的嵌套包(比如 a/b/c/d.py),否则导入路径太长,维护起来麻烦——如果功能确实很细,可以考虑拆成多个独立的小项目,或者用命名空间包。
6. 实战:从零搭建一个小工具包
我们来写一个简单的 simple_tools 工具包,包含字符串和数学两个子模块,再用入口文件测试。
6.1 最终的包结构
6.2 核心代码
(1)simple_tools/str_utils.py(字符串工具模块)
(2)simple_tools/math_utils.py(数学工具模块)
(3)simple_tools/__init__.py(简化导入+定义公开范围)
(4)main.py(入口测试文件)
6.3 运行效果
直接在终端打开 simple_tools_demo 目录,运行:
就能看到测试结果啦!
7. 现代Python的模块小彩蛋
7.1 命名空间包(Python 3.3+)
如果你有多个目录,想让它们共用一个包名(比如插件系统),可以用命名空间包——只要去掉所有目录里的 __init__.py,Python就会自动把它们合并成一个包。
7.2 模块缓存清理
有时候修改了模块代码,但运行入口文件还是旧结果——这是因为 .pyc 缓存没更新。手动清理的方法很简单:
- 删除模块所在目录的
__pycache__文件夹 - 或者在Python开头加
import importlib; importlib.reload(你的模块名)(但只在开发调试时用,正式代码别加)
总结
模块和包是Python构建大型项目的基础,核心就是一句话:把代码拆成逻辑清晰的小单元,按目录结构分类,用规范的导入方式调用。
现在赶紧回去把你堆成山的 main.py 拆了吧!拆完你会发现,写代码的快乐又回来了😎

