A、Python包管理
前置说明:Python 中「包 (package)」和「模块 (module)」的基础概念
- **模块 (module)**:单个
.py文件就是一个模块,里面的函数 / 类 / 变量可以被导入复用- 包 (package):包含
__init__.py文件的文件夹就是一个包,包里面可以放多个.py模块 / 子包,是 Python 组织项目代码的核心方式
一、Python 最基础的【3 种核心导包方式】
所有导入语法都基于关键字 import 和 from ... import ...,是 Python 最原生、最常用的写法,无任何限制,所有 Python 版本通用。
1.1 方式 1:直接导入【整个模块】 import 模块名
1 | import 模块名 |
1 | # 导入Python内置标准库模块 |
适用场景
- 导入 Python 内置标准库(os/sys/json/time 等)
- 导入第三方安装的库(requests/pandas/boto3 等,比如你 AWS 开发用的 boto3/aws-cdk-lib)
- 导入自己项目中的单个
.py模块,且需要使用模块中多个函数 / 类
1.2 方式 2:从模块中【导入指定内容】 from 模块名 import 成员
1 | from 模块名 import 函数名/类名/变量名 |
1 | # 从内置模块导入指定成员 |
核心优势
- 代码极度简洁,调用时不用写模块名前缀
- 按需导入,只加载需要的函数 / 类,不加载整个模块(对大模块友好)
- 只需要使用模块中的部分函数 / 类 / 变量,是项目开发中使用频率最高的导包方式
1.3 方式 3:从模块中【导入所有内容】 from 模块名 import *
和第二种没区别
1 | from 模块名 import * |
1 | from os import * |
优点:最简洁,不用写任何前缀,快速测试 / 临时脚本可用
缺点 1:命名冲突风险极高!如果导入的两个模块有同名函数,后导入的会覆盖先导入的,排查难度极大
缺点 2:代码可读性极差!别人看你的代码,完全不知道某个函数是从哪个模块导入的
缺点 3:会导入模块中所有成员,包括你不需要的,造成内存浪费
二、Python包的导入(绝对导入 + 相对导入)
当你的项目不是单个.py文件,而是多文件夹、多模块的工程化结构时,就必须用到「包的导入」,分为 绝对导入 和 相对导入 两种。
2.1 前置准备:标准 Python 项目结构
所有示例都基于这个最规范的项目结构,创建在你的 EC2 服务器 /home/ec2-user/iot-portal2 目录下,结构如下:
1 | iot-portal2/ # 项目根目录(项目包的根) |
关键规则:没有
__init__.py的文件夹,Python 不会把它识别为「包」,无法被导入!补充:Python3.3+ 支持「命名空间包」可以不用
__init__.py,但项目开发强烈建议保留,兼容性最好,规范统一。
2.1 核心 1:绝对导入( 推荐!99% 项目首选,官方最佳实践)
2.1.1 什么是绝对导入
从「项目根目录」出发,完整写出要导入的「包.模块.成员」路径,写法是 from 包名 import 模块 或 **from 包名.模块 import 成员**,是 Python 官方推荐的导包方式。
核心特点
- 路径完整、清晰,可读性极强,任何人看代码都知道导入的内容来自哪里
- 无层级限制,项目任意位置的模块,都可以用绝对导入互相引用
- 不会出现导入报错,是最稳定、最不容易踩坑的写法
- 支持在任意位置运行代码(比如直接运行模块、从入口文件运行)
1 | # ========== 示例1:在 main.py 中导入 app包下的模块/成员(最常用) |
2.2 核心 2:相对导入( 仅限「包内部模块互导」)
2.2.1 什么是相对导入
从「当前模块所在位置」出发,用「点 .」表示层级关系,导入「同包 / 子包 / 父包」的模块,是包内部的「相对路径导入」,写法是 from . import 模块 或 **from ..包名 import 模块**。
2.2.2 核心规则
- 点的含义:
./代表「当前所在包」,../代表「上级包」,../../代表「上上级包」,和 Linux 的目录层级完全一致 - 适用范围:只能在「包内部的模块」中使用,绝对不能在项目入口文件(比如 main.py)中使用!
- 运行限制:使用了相对导入的模块,不能直接运行这个模块文件(
python app/api.py),只能通过「项目入口文件(main.py)」运行(python main.py),否则会报ImportError: attempted relative import with no known parent package错误! - 优点:不用写完整路径,包内部互导更简洁,包的目录结构变动时,相对导入的代码不用改
1 | # ========== 示例1:在 app/api.py 中导入同包下的 utils.py 模块(最常用) |
2.2.3 绝对导入 vs 相对导入 选型建议
✅ 优先用绝对导入:适合所有场景,稳定、清晰、无坑,项目越大越能体现优势(你的 iot-portal2 必用)
✅ 补充用相对导入:仅在「包内部的模块互导」时使用,简化代码
❌ 禁止混合使用:同一个项目中不要混用两种写法,会导致代码混乱
三、Python 进阶导包写法
3.1 技巧 1:导入包时指定「导入的内容」(通过__init__.py优化导包)
在包的 __init__.py 文件中,可以提前定义 __all__ 变量,或者提前导入模块 / 成员,这样外部导入这个包时,就可以直接导入需要的内容,不用写深层路径,简化导包代码。
3.1.1 实战示例
在 app/__init__.py 文件中写入:
1 | # 方式1:定义__all__,指定 from app import * 时要导入的模块 |
之后在 main.py 中就可以这样导入,代码更简洁:
1 | from app import get_device_info, format_data |
3.2 技巧 2:导入模块时忽略「命名冲突」(解决重名问题)
开发中经常遇到:不同模块有同名的函数 / 类,导入后会覆盖,这时候用 as 别名 完美解决。
1 | # 场景:app/utils.py 有一个get_data(),config/settings.py也有一个get_data() |
3.3 技巧 3:动态导入模块(按需导入)
通过 Python 内置的 importlib 模块,可以在代码运行时动态导入模块 / 成员,而不是在文件开头静态导入,适合:需要根据条件加载不同模块、项目模块太多不想全部加载、插件化开发等场景。
1 | import importlib |
四、Python 导包的【搜索路径】与【优先级规则】
4.1 核心问题:Python 导入模块时,到底在「哪里找」这个模块?
新手最头疼的就是导包时报 ModuleNotFoundError: No module named 'xxx',本质原因就是:Python 在「导包搜索路径」中,找不到你要导入的模块 / 包。
4.1.1 Python 的导包搜索路径(优先级从高到低,按顺序查找)
Python 会按照以下顺序,在这些路径中查找要导入的模块,找到即停止,找不到就报错:
- 当前运行脚本的所在目录(最高优先级)
- Python 的内置标准库路径(比如
/usr/lib/python3.9/) - 第三方库安装路径(比如
/home/ec2-user/.local/lib/python3.9/site-packages/,pip 安装的库都在这里) - 系统环境变量
PYTHONPATH配置的路径 - 项目的包路径
4.1.2 查看 Python 的导包搜索路径(调试报错必备命令)
1 | import sys |
4.2 解决「导入报错」的终极方案
如果你的项目在 EC2 上运行时,报 ModuleNotFoundError,按以下顺序排查,100% 能解决:
4.2.1 方案 1:把项目根目录添加到导包路径(最常用,推荐)
在项目入口文件 main.py 的最顶部,添加以下代码,把项目根目录加入 Python 的搜索路径:
1 | import sys |
4.2.2 方案 2:设置系统环境变量 PYTHONPATH(永久生效)
在 EC2 的 ~/.bashrc 文件中添加项目路径,永久生效,适合你的服务器环境:
1 | # 编辑环境变量文件 |
五、Python 导包【避坑指南】(新手必看,少走 99% 的弯路)
5.1 坑 1:包名 / 模块名 和 Python 内置模块重名(比如写了一个 os.py)
后果:导入的是你自己的模块,不是 Python 内置的,会报各种奇怪的错误
解决方案:绝对不要用 Python 内置模块名 / 第三方库名 命名自己的文件 / 包(比如 os.py、sys.py、requests.py、boto3.py)
5.2 坑 2:相对导入在入口文件中使用,或直接运行使用了相对导入的模块
后果:报 ImportError: attempted relative import with no known parent package
解决方案:入口文件只用绝对导入;相对导入只在包内部使用,且只能通过入口文件运行项目
5.3 坑 3:忘记在文件夹中创建 __init__.py 文件
后果:Python 不识别这个文件夹为包,报 ModuleNotFoundError
解决方案:所有需要被导入的文件夹,都加上空的 __init__.py 文件
5.4 坑 4:导入时大小写错误(Python 大小写敏感)
后果:报 ModuleNotFoundError,比如 from App import api 但实际包名是 app
解决方案:严格区分大小写,包名 / 模块名全部用小写(Python 官方规范)
5.5 坑 5:用 from 模块 import * 导致命名冲突
后果:后导入的函数覆盖先导入的,排查难度极大
解决方案:项目中禁用这种写法,改用 from 模块 import 成员 按需导入
5.6 坑 6:项目目录结构变动后,没有更新导入路径
后果:报 ModuleNotFoundError
解决方案:用绝对导入,目录结构变动时,只需要修改导入路径即可,相对导入则不用改
5.7 坑 7:在 EC2 上用 pip 安装库后,运行时提示模块找不到
后果:报 ModuleNotFoundError,比如安装了 requests,但运行时找不到
解决方案:EC2 上可能有多个 Python 版本,确保安装和运行的是同一个版本:
1 | # 安装时指定Python版本 |
5.8 坑 8:循环导入(A 模块导入 B,B 模块又导入 A)
后果:报 ImportError: cannot import name 'xxx' from partially initialized module 'xxx'
解决方案:把导入语句写在函数内部(局部导入),或者把公共代码抽离到新的模块中
六、Python 导包【命名规范】(官方 PEP8 规范)
Python 官方有严格的导包命名规范(PEP8),遵循后代码可读性极高,团队协作无冲突,也是大厂的开发规范:
- 导包语句全部写在文件的 最顶部,注释之后、业务代码之前
- 导包顺序:内置标准库 → 第三方库 → 项目本地包 / 模块,用空行分隔
- 禁止一行导入多个模块(除了极简单的内置库)
- 包名 / 模块名全部用 小写字母,多个单词用下划线分隔(比如
data_utils.py) - 导入时尽量用
from 模块 import 成员,按需导入,减少内存占用 - 遇到命名冲突时,必须用
as起别名,不要修改原成员名
1 | # -*- coding: utf-8 -*- |
七、总结
7.1 核心导包方式(按优先级排序)
- 基础导包:
import 模块/from 模块 import 成员→ 90% 基础场景够用 - 项目导包:绝对导入(推荐)/ 相对导入 → 工程化项目核心写法
- 进阶技巧:
as 别名解冲突、__init__.py优化导包、动态导入 → 提升开发效率
7.2 核心避坑原则
- 项目开发优先用绝对导入,稳定无坑;相对导入仅用于包内部
- 包必须有
__init__.py,模块名不要和内置库重名 - 导入报错先查
sys.path,把项目根目录加入搜索路径即可解决 - 严格遵循 PEP8 导包规范,代码可读性拉满
B、自建 Python 基础代码 共享给其他项目
抽离了一份自研的 Python 基础代码(工具类 / 公共函数 / 通用配置 / 基础逻辑),想让多个 Python 项目都能直接复用这份代码,不用复制粘贴、不用重复维护,改一处就能所有项目生效。
1. 需求
- 被共享的「基础代码」一份源码,所有项目共用
- 新项目想使用时,一行导入语句 即可调用(如
from my_utils import func1) - 基础代码更新后,所有项目自动生效(或极简更新)
- 支持任意目录的项目调用,不受项目位置限制
2. 前置准备:统一你的「基础代码」目录结构
不管用哪种方案,先把你的自建基础代码整理成标准结构,这是共享的前提,所有方案都基于这个结构,假设你的基础代码叫「python_base_lib」(基础库),结构如下:
这是最通用的结构,哪怕你的基础代码只有 1 个
.py文件也适用,必须按这个来
1 | python_base_lib/ # 你的基础代码根目录(自定义名字,比如my_base、utils_lib都可以) |
补充:如果你的基础代码只有 1 个
.py文件(比如my_single_utils.py),那就创建一个文件夹把它包起来,里面加一个空的__init__.py即可,变成上面的结构。
3.方案一:直接添加「导包路径」(推荐:开发阶段)
3.1 适用场景
个人开发 / 小团队、2~5 个项目共享 这份基础代码
基础代码改动频率高、需要随时更新随时生效
不想做任何复杂配置,5 分钟配置完成、立即可用
所有项目都部署在同一台服务器 / 本机(比如你的 AWS EC2 上多个项目共享)
3.2 核心原理
Python 导入模块时,会从 sys.path 这个列表里的路径去查找模块 / 包;我们只需要把「基础代码的根目录」添加到 Python 的全局导包路径,所有 Python 项目就都能像导入第三方库一样,直接导入这份基础代码。
3.3 方式 1:临时生效(单个项目内配置,项目独享,适合测试)
在需要调用基础代码的项目的「入口文件」最顶部,添加以下代码,仅此 3 行,写完直接导入即可:
1 | # 比如你的项目入口文件 main.py 最顶部 |
✅ 优点:零配置、不污染全局、只对当前项目生效
✅ 缺点:每个需要共享的项目都要加这 3 行代码
3.4 方式 2:永久生效(全局配置,所有项目通用)
在你的服务器(EC2)上配置系统环境变量 PYTHONPATH,把基础代码路径加入,配置一次,所有 Python 项目永久可用,一劳永逸,这是开发中最常用的方式!
1 | EC2的Amazon Linux / CentOS / Ubuntu 都用这个命令 |
验证效果
1 | python3 |
看到输出的列表里有你的/home/ec2-user/python_base_lib路径,就是配置成功!
配置成功后,所有项目「无缝导入」使用
任何 Python 项目,不用加任何代码,直接导入即可,体验和导入requests一样丝滑:
1 | # 任意项目的任意.py文件里,直接写 |
3.5 优缺点总结
✅ 优点:最简单、零配置、零学习成本、立即生效、代码无侵入,适合所有开发场景,改基础代码后所有项目立即生效,不用任何更新操作
✅ 缺点:仅适用于「同一台机器」的项目共享(如果你的项目部署在多台服务器,需要每台都配置一次)
✅ 适用优先级:⭐⭐⭐⭐⭐ (优先用这个方案,满足 90% 的个人 / 小团队需求)
4. 方案二:做成「本地 pip 包」
4.1 适用场景
你的基础代码已经稳定、成熟,改动频率低
想让这份代码的导入体验完全和第三方库一致(比如pip install requests)
多人协作开发、多台服务器部署、需要标准化管理版本
想把这份基础代码做成团队的「私有基础库」,规范统一
4.2 核心原理
把你的「基础代码包」打包成 Python 标准的 wheel 包,然后通过pip命令本地安装到 Python 环境中,安装后,所有项目可以无缝导入,和用requests/boto3没有任何区别。
✅ 本质:你的基础代码,变成了「自己的第三方库」,pip 安装后会被放到 Python 的
site-packages目录(第三方库默认目录),全局可用。
4.3 完整实操步骤(6 步完成,全复制命令即可,无难度)
你的基础代码目录结构:python_base_lib/(带__init__.py)
✔️ 步骤 1:在基础代码根目录,新建一个 setup.py 文件
这是 Python 打包的核心配置文件,复制以下代码粘贴即可,只需要改name/version/author这 3 个自定义项,其他不用动!
1 | # python_base_lib/setup.py 完整代码 |
此时你的基础代码目录结构变成:
1 | python_base_lib/ |
✔️ 步骤 2:安装打包依赖(EC2 上执行一次即可)
1 | pip3 install setuptools wheel build |
✔️ 步骤 3:在基础代码根目录,执行打包命令
1 | cd /home/ec2-user/python_base_lib |
✅ 执行成功后,会在目录下生成 dist/ 和 *.egg-info/ 两个文件夹,dist/里就是打包好的安装包(.whl格式)。
✔️ 步骤 4:本地 pip 安装你的基础包(核心步骤)
1 | # 本地安装,加 -e 参数是【开发模式】,强烈推荐!!! |
✅ 关键:
-e是editable的缩写,意思是「可编辑模式」:安装后,你的基础代码源码不用动,如果修改了基础代码,所有项目立即生效,不用重新打包安装!如果不加-e,修改源码后需要重新打包安装才生效。
✔️ 步骤 5:验证安装成功
1 | pip3 list | grep python_base_lib |
✅ 能看到你的包名和版本号,就是安装成功!
✔️ 步骤 6:所有项目无缝导入使用
任何 Python 项目,任何位置,直接导入即可,完美体验:
1 | # 和导入第三方库完全一样 |
4.4 方案二 优缺点总结
✅ 优点:最规范、最优雅,Python 官方推荐的包管理方式;导入体验完美;支持版本管理;多人协作无冲突;适合长期维护的基础代码
✅ 缺点:比方案一多了几步打包配置(但配置一次永久可用)
✅ 适用优先级:⭐⭐⭐⭐ (基础代码稳定后,首选升级为这个方案)
5. 方案三:软链接(符号链接)共享(推荐:同服务器、多项目同目录)
5.1 适用场景
✅ 所有需要共享代码的项目,都放在同一目录下(比如 EC2 的/home/ec2-user/projects/下有iot-portal2、project_a、project_b)
✅ 不想改 Python 路径、不想打包,只想做「目录关联」
✅ 基础代码和项目在同一台服务器,目录结构固定
5.2 核心原理
Linux 系统的软链接(symbolic link):相当于给你的基础代码目录,在每个项目里创建一个「快捷方式」,项目里导入这个「快捷方式」,就等于导入了真实的基础代码,本质还是一份源码,改一处所有项目生效。
5.3 实操步骤
假设你的目录结构如下:
1 | /home/ec2-user/ |
给每个业务项目创建软链接
1 | # 进入iot-portal2项目目录,创建软链接 |
✅ 执行后,每个项目目录里都会出现一个python_base_lib的「快捷方式文件夹」,和真实的文件夹一模一样。
✅ 项目内导入使用
1 | # 在iot-portal2的任意.py文件里 |
5.4 方案三 优缺点总结
✅ 优点:Linux 原生功能、零配置、零 Python 代码修改、无缝导入
✅ 缺点:每个项目都要创建一次软链接;仅适用于同服务器同目录的项目
✅ 适用优先级:⭐⭐⭐
6. 方案四:【临时应急】把基础代码包 直接放入项目目录
6.1 适用场景
✅ 临时应急:比如项目要快速部署、没有权限配置服务器环境变量
✅ 项目很少、基础代码几乎不更新
6.2 实现方式
直接把你的python_base_lib文件夹,复制粘贴到需要使用的项目根目录里,目录结构如下:
1 | iot-portal2/ |
1 | from python_base_lib.common_utils import func1 |
6.3 方案四 优缺点总结
✅ 优点:零配置、零依赖、部署方便
❌ 缺点:维护灾难!基础代码改一处,要同步复制到所有项目,极易出错;项目体积变大;不推荐长期使用,仅应急!
7. 方案五:搭建私有 PyPI 源
7.1 适用场景
✅ 公司团队开发、几十上百个项目 / 多台服务器共享这份基础代码
✅ 有专人维护基础代码、需要严格的版本管理、权限控制
✅ 想让团队成员像安装pip install requests一样,安装你的私有基础库
7.2 核心原理
搭建一个公司内部的私有 PyPI 仓库(类似 Python 官方的 pip 源),把你的基础代码包上传到私有源,团队成员在任何服务器上,只需要执行pip install -i 私有源地址 python_base_lib,就能安装使用,和用官方库完全一致。
7.3 常用工具
- 轻量版:
pypiserver(Python 写的,一键搭建,推荐) - 企业版:
devpi/Sonatype Nexus
7.4 优缺点总结
✅ 优点:最规范的团队级共享方案、版本管理严格、权限可控、跨服务器跨团队共享、体验完美
✅ 缺点:需要搭建和维护私有服务,有一定的学习成本和运维成本
✅ 适用优先级:⭐⭐⭐⭐⭐ (团队开发首选)
8. 按你的场景【最优选型建议】(重中之重,帮你直接定方案)
结合你的实际情况:AWS EC2 服务器 + 自研基础代码 + 多个 Python 项目(比如 iot-portal2) + 个人 / 小团队开发,按优先级排序,你就从下面 2 个方案里二选一即可,不用纠结:
8.1 首选方案:【方案一 全局配置 PYTHONPATH】
✅ 理由:零配置、零成本、5 分钟搞定、永久生效、改代码立即同步,完美匹配你的场景,99% 的个人 / 小团队都用这个方案,足够用了!
8.2 备选升级方案:【方案二 本地 pip 包安装】
✅ 理由:如果你的基础代码已经稳定,很少改动,想让导入体验更规范,就用这个方案,配置一次,终身受益,体验和第三方库一致!
9. 补充:共享代码的【最佳实践 & 避坑指南】
9.1 一、共享代码的目录 / 代码规范(避免导入报错)
- 你的基础代码包,必须包含
__init__.py文件(空文件也可以),否则 Python 识别不了是包,会报ModuleNotFoundError - 基础代码的文件名 / 包名全部用小写 + 下划线(比如
common_utils.py,不要用CommonUtils.py),符合 Python 官方规范,避免大小写导入错误 - 基础代码里不要有循环导入(比如 A 导入 B,B 又导入 A),会导致所有项目都报错
- 基础代码里的绝对路径要改成相对路径(比如读取配置文件),避免在其他项目里运行时路径找不到
9.2 二、更新共享代码后,各方案的生效规则(核心!)
- 方案一(PYTHONPATH)/ 方案三(软链接):改完基础代码,所有项目立即生效,不用任何操作 ✅
- 方案二(本地 pip 安装,带
-e):改完基础代码,所有项目立即生效 ✅(这就是为什么推荐加-e) - 方案二(本地 pip 安装,不带
-e):改完基础代码后,需要重新打包 + 重新安装才生效 - 方案四(复制粘贴):改完基础代码后,需要重新复制到所有项目 ❌
9.3 三、跨项目导入的「导入语句」优先级建议
不管用哪种方案,建议你的导入语句写成:
1 | # 推荐写法:带包名导入,清晰明确,无冲突 |