Python模块:导入、自定义模块、包与第三方库安装
学完函数、容器、高级特性和函数式编程之后,接下来要解决一个非常实际的问题:当代码越来越多时,应该如何组织它们?
如果把所有函数、变量和业务逻辑都堆在一个 .py 文件里,刚开始也许还能勉强维护,但一旦项目变大,代码就会变得难读、难改、难复用。这时,模块(module) 和 包(package) 就成为 Python 编程里非常重要的组织方式。
本文会围绕以下几个问题展开:
- 什么是模块,为什么要使用模块;
import到底做了什么;- 如何编写自己的模块;
- 什么是包,如何用目录组织模块;
- Python 是如何查找模块的;
- 如何安装和使用第三方模块。
理解这一章之后,你就不再只是“会写一个 Python 文件”,而是开始具备组织一个小型 Python 项目的能力。
一、为什么需要模块?
在前面的学习里,我们写的大多数代码都放在一个文件中,例如:
1 | print('Hello, Python!') |
这种写法在入门阶段完全没问题,但如果项目逐渐变大,就会遇到几个现实问题:
- 代码难以维护:一个文件过长后,查找逻辑会越来越痛苦;
- 功能难以复用:如果别的程序也想使用其中某个函数,复制粘贴显然不是好办法;
- 命名容易冲突:大家都把函数写在一个文件里,名字重复的概率会越来越高;
- 协作困难:多人开发时,不同功能最好拆成不同文件,否则冲突频繁。
这时就需要把代码拆分成多个模块。
你可以把模块理解为:
一个
.py文件,就是一个 Python 模块。
例如:
math_utils.pystring_tools.pyuser_service.py
它们都可以被其他 Python 文件导入并使用。
二、什么是模块?
最简单地说,一个 Python 源文件就是一个模块。
例如我们创建一个文件 greet.py:
1 | def say_hello(name): |
然后在另一个文件 main.py 中导入它:
1 | import greet |
运行 main.py 时,就可以使用 greet 模块中定义的函数。
2.1 使用模块的核心价值
模块最大的价值不只是“把代码拆开”,而是让程序具备更好的结构。
例如,一个小项目可以这样划分:
1 | project/ |
其中:
main.py负责程序入口;db.py负责数据库相关逻辑;user.py负责用户业务;utils.py负责通用工具函数。
这样一来,职责会清晰很多。
2.2 模块和函数的关系
函数解决的是“复用一段逻辑”;
模块解决的是“组织一组相关代码”。
你可以这样理解:
- 函数:代码复用的最小单位;
- 模块:代码组织的基本单位;
- 包:更高层级的模块组织方式。
三、import 到底做了什么?
学习模块时,最常见的语法就是 import。
3.1 最基本的导入方式
1 | import math |
这里:
import math表示导入标准库中的math模块;- 后续通过
math.sqrt()、math.pi访问模块里的函数和变量。
3.2 为什么导入后要加模块名前缀?
因为 import math 导入的是整个模块对象。
所以你拿到的是:
- 模块名
math - 模块里的成员
sqrt - 模块里的成员
pi
也就是说:
1 | math.sqrt(16) |
本质上是在“模块命名空间”里找 sqrt。
这样做有两个明显好处:
- 可读性好:你一眼就知道
sqrt来自math; - 避免冲突:不同模块里都可能有同名函数。
例如:
1 | import random |
即使两个模块里以后都出现某个同名标识符,也不会直接打架,因为访问时都带模块前缀。
3.3 from ... import ... 的写法
有时你不想每次都写模块名前缀,可以这样导入:
1 | from math import sqrt, pi |
这样写更短,但也要注意:
- 导入的名字会直接进入当前命名空间;
- 如果导入太多,容易造成名字冲突;
- 阅读代码时,不如
math.sqrt()那样直观。
所以工程实践里通常建议:
- 模块级功能较多时,优先用
import module; - 只使用少量固定成员时,可以用
from module import name。
3.4 起别名:as
如果模块名太长,或者想避免冲突,可以起别名:
1 | import numpy as np |
这在数据分析领域几乎是约定俗成的写法。
普通模块也一样:
1 | import datetime as dt |
对于导入成员,也能起别名:
1 | from math import sqrt as square_root |
3.5 不推荐滥用 from module import *
你可能还见过这样的写法:
1 | from math import * |
这会把模块里的很多名字直接导入当前作用域。
虽然写起来省事,但问题很明显:
- 你不容易知道某个名字来自哪里;
- 容易覆盖当前已有变量;
- 在大型项目里很难维护。
所以通常不建议在正式代码里使用这种写法。
四、自定义模块:把自己的代码拆出去
理解模块最好的方式,就是自己写一个模块。
4.1 一个简单的示例
新建文件 calc_utils.py:
1 | def add(x, y): |
然后在 main.py 中使用它:
1 | import calc_utils |
4.2 模块执行与模块导入的区别
很多初学者会疑惑:
一个文件既可以直接运行,也可以被别人导入,那该怎么区分?
这就要用到 __name__。
1 | def test(): |
- 如果你直接运行这个文件,
__name__的值是__main__; - 如果这个文件被别的文件导入,
__name__的值通常是模块名。
所以常见写法是:
1 | def test(): |
这表示:
- 直接运行当前文件时,执行
test(); - 把当前文件作为模块导入时,不执行这段测试代码。
这是 Python 模块里非常重要的一种写法。
4.3 一个更完整的模块示例
1 | #!/usr/bin/env python3 |
这里还出现了几个常见内容:
- 模块说明字符串;
__author__作者信息;main()作为程序入口;if __name__ == '__main__':作为可执行判断。
这些内容不是强制要求,但在教程和示例代码里非常常见。
五、模块中的作用域与“公开 / 非公开”约定
模块中定义的变量、函数,本质上也有“是否希望给外部使用”的区别。
5.1 正常命名:默认可被外部访问
1 | def public_func(): |
这种名字没有特殊限制,导入模块后可以直接访问。
5.2 以下划线开头:表示内部使用
1 | def _private_func(): |
在 Python 中,以下划线开头是一种约定:
public_func():公开接口;_private_func():内部实现细节,不建议模块外直接使用。
注意,这不是“强制私有”,只是社区约定。
也就是说你仍然可以这样调用:
1 | import mymodule |
语法上没问题,但从设计上不推荐。
5.3 双下划线并不是模块级常规做法
像 __name__、__author__、__doc__ 这种双下划线名字,通常是 Python 预留或约定的特殊变量。
在模块里,我们更常见的是:
__name____file____doc____author__
而不是自己大量造双下划线名字。
5.4 对外暴露接口的思路
模块设计时,一个很好的习惯是:
- 把对外提供的能力放在公开函数中;
- 把内部辅助逻辑写成
_helper()这类内部函数。
例如:
1 | def _format_name(name): |
这样模块边界会更清晰。
六、包(Package):用目录组织多个模块
当模块继续变多时,只靠平铺多个 .py 文件还不够,这时就要使用包。
6.1 什么是包?
简单理解:
包就是包含多个模块的目录结构。
例如:
1 | mycompany/ |
这里:
mycompany是一个包;abc.py和xyz.py是这个包里的模块。
那么它们的模块名就变成了:
mycompany.abcmycompany.xyz
6.2 为什么要有 __init__.py?
在传统习惯里,一个目录下放上 __init__.py,Python 就会把它识别为包。
例如:
1 | shop/ |
导入时可以这样写:
1 | import shop.user |
或者:
1 | from shop import user |
虽然在现代 Python 中,命名空间包让某些场景不一定强制需要 __init__.py,但对于初学者和普通项目来说,保留 __init__.py 依然是更清晰、更稳妥的做法。
6.3 多层包结构
项目稍大一点时,经常会使用多层目录:
1 | mycompany/ |
这时模块名分别可能是:
mycompany.utilsmycompany.web.apimycompany.web.views
这种层级结构有助于把不同领域的代码拆开。
6.4 包的实际意义
包的价值主要在于:
- 进一步避免命名冲突;
- 更适合组织中大型项目;
- 让模块之间的归属关系更清楚。
比如两个不同目录下都可以有 utils.py:
mycompany.utilsmycompany.web.utils
只要包路径不同,它们就不会冲突。
七、模块搜索路径:Python 去哪里找模块?
当你写下:
1 | import mymodule |
Python 解释器并不是“凭空知道”这个模块在哪,它会按照一定规则去查找。
7.1 sys.path 是关键
模块搜索路径通常保存在 sys.path 中:
1 | import sys |
常见包含内容有:
- 当前运行目录;
- Python 标准库目录;
- 第三方库安装目录(如
site-packages); - 你手动添加的路径。
7.2 为什么有时会报 ModuleNotFoundError?
如果 Python 在搜索路径里找不到目标模块,就会报错:
1 | import mymodule |
可能得到:
1 | ModuleNotFoundError: No module named 'mymodule' |
这通常意味着:
- 模块文件根本不存在;
- 文件在错误目录;
- 当前运行路径不对;
- 包结构不完整;
- 模块名写错了。
7.3 临时修改 sys.path
有时你会看到这样的代码:
1 | import sys |
这样可以临时把某个目录加入模块搜索路径。
它在调试时有用,但通常不建议作为长期方案,因为:
- 可移植性差;
- 运行环境一变就可能失效;
- 容易让项目结构混乱。
7.4 更推荐的方式:组织好项目结构
与其频繁修改 sys.path,更推荐:
- 正确组织包结构;
- 在项目根目录运行程序;
- 使用虚拟环境;
- 通过标准安装方式管理依赖。
7.5 PYTHONPATH 的作用
除了运行时修改 sys.path,还可以通过环境变量 PYTHONPATH 扩展搜索路径。
不过对初学者来说,更重要的是先理解:
Python 会按照既定搜索路径查找模块,而不是扫描你电脑上的所有文件。
只要明白这一点,很多导入报错就容易定位了。
八、标准库、第三方库和自定义模块
学习模块时,最好把模块来源分清楚。
8.1 标准库模块
标准库是 Python 自带的模块,安装 Python 后就能直接用。
例如:
sysosmathdatetimejsonpathlib
示例:
1 | import os |
8.2 第三方模块
第三方模块不是 Python 自带的,需要额外安装。
例如:
requestspandasnumpyflaskpillow
如果未安装直接导入,就会报错。
8.3 自定义模块
你自己写的 .py 文件,也属于模块。
所以从来源来看,Python 模块大体有三类:
- 标准库模块;
- 第三方模块;
- 自定义模块。
这个分类非常重要,因为它决定了模块应该如何获取和管理。
九、安装第三方模块:用 pip 管理依赖
当项目依赖外部库时,最常见的安装方式是 pip。
9.1 最基础的安装命令
例如安装 requests:
1 | pip install requests |
或者在某些环境中使用:
1 | pip3 install requests |
为了更明确地指定 Python 解释器,也常见这样写:
1 | python -m pip install requests |
这通常比直接写 pip 更稳妥。
9.2 安装后如何使用
1 | import requests |
9.3 常见安装问题
1. 找不到 pip 命令
可能是:
- Python 没正确安装;
pip没加入环境变量;- 当前环境里命令名不是
pip。
这时可以优先尝试:
1 | python -m pip --version |
2. 安装到了别的 Python 环境
这是初学者非常常见的问题。
例如你运行程序用的是一个 Python,安装依赖时却给另一个 Python 安装了包。结果就是:
pip install看起来成功了;- 但
import还是报错。
所以推荐尽量使用:
1 | python -m pip install 包名 |
让安装动作明确绑定到当前解释器。
3. 权限不足
在某些环境下,可能需要:
1 | pip install --user requests |
或者使用虚拟环境来隔离依赖。
9.4 依赖管理意识要尽早建立
安装第三方模块,不只是“能用就行”,还要逐渐建立这样的意识:
- 这个包是项目依赖;
- 这个依赖需要版本约束;
- 换一台机器时,也要能重新装回来。
这也是后面学习虚拟环境和 requirements.txt 的基础。
十、虚拟环境:让不同项目的依赖互不干扰
虽然本节重点是模块,但一讲第三方模块,就很难绕开虚拟环境。
10.1 为什么需要虚拟环境?
假设你有两个项目:
- 项目 A 依赖
requests==2.28 - 项目 B 依赖
requests==2.31
如果都装在系统全局环境里,就可能互相影响。
虚拟环境的作用就是:
给每个项目提供一个相对独立的 Python 运行环境。
10.2 使用 venv 创建虚拟环境
1 | python -m venv venv |
这会创建一个名为 venv 的虚拟环境目录。
激活方式通常是:
1 | source venv/bin/activate |
Windows 下则可能是:
1 | venv\Scripts\activate |
激活后再安装依赖:
1 | python -m pip install requests |
这样依赖就只安装在当前项目环境里。
10.3 初学者对虚拟环境的理解重点
你不必一开始就把虚拟环境研究得很深,但至少要知道:
- 它能隔离项目依赖;
- 它能减少环境污染;
- 它是实际开发里非常常见的基础设施。
十一、综合示例:组织一个小型 Python 项目
下面用一个简单例子,把模块和包的思路串起来。
项目结构:
1 | weather_app/ |
formatter.py:
1 | def format_city_name(city): |
service.py:
1 | from app.formatter import format_city_name |
main.py:
1 | from app.service import get_weather_message |
这个例子虽然很小,但已经体现了几个关键点:
- 使用包来组织模块;
- 使用
from ... import ...在模块间协作; - 用
if __name__ == '__main__':作为程序入口; - 把“格式化逻辑”和“业务逻辑”拆到不同模块。
这就是模块化编程最核心的价值:拆分职责,保持结构清晰。
十二、学习建议:从“能导入”走向“会组织”
学这一章时,很多人容易把重点只放在 import 语法本身上。其实更重要的是建立模块化思维。
12.1 先掌握最常用的三种导入方式
优先熟悉下面三种:
1 | import math |
这三种已经覆盖了大多数日常使用场景。
12.2 理解 __name__ == '__main__' 的意义
这是“文件可执行”和“文件可导入”之间的桥梁。
如果你真正理解了这一点,就会明白:
- Python 文件既可以是脚本;
- 也可以是模块;
- 二者并不冲突。
12.3 不要把所有工具函数都塞进一个 utils.py
初学者很容易把所有杂项函数都扔进一个 utils.py。开始时可以接受,但当项目变大后,最好继续拆分,例如:
string_utils.pyfile_utils.pydate_utils.py
这样会比“一个大杂烩模块”更容易维护。
12.4 安装第三方模块时注意解释器一致性
记住这条经验:
如果
pip install成功了,但import还是报错,优先怀疑是不是装到了别的 Python 环境里。
这在真实开发里非常常见。
十三、总结
这一篇我们系统梳理了 Python 模块相关的核心内容:
- 模块本质上就是一个
.py文件; - 使用模块可以提升代码复用性、可维护性和组织性;
import会把模块导入当前程序,并通过命名空间访问其成员;from ... import ...更简洁,但要注意命名冲突;if __name__ == '__main__':用于区分“直接运行”和“被导入”;- 以
_开头的名字通常表示模块内部使用; - 包是组织多个模块的目录结构;
- Python 会通过
sys.path等搜索路径查找模块; - 第三方模块通常通过
pip安装; - 虚拟环境可以帮助不同项目隔离依赖。
如果说前面几篇更多是在学习“单文件里的 Python 写法”,那么这一篇开始,你已经进入“如何组织一个 Python 项目”的阶段。
下一步继续往后学时,最自然的衔接方向通常是:
- 面向对象编程:学习类、实例、继承和多态;
- 或者 错误处理:学习
try / except / finally、调试和测试。
这两条线都会频繁和模块化代码结合使用。