python 搜索模块解析

目录

(Module Resolution / sys.path 搜索机制)

介绍

当你运行:

python a.py

Python 会依次在以下路径中查找模块:

✅ 查找顺序是  sys.path

import sys
print(sys.path)
[
  '',                       # 当前工作目录或脚本目录
  '/your/project/path',     # 执行时添加的路径 PYTHONPATH
  '/usr/local/lib/python3.x/site-packages', # 第三方包目录
  ...
]

✅ 二、模块搜索路径来自哪里?(sys.path是如何被构建的?)

来源 说明
当前工作目录(通常是 .) 也就是你执行 python script.py 的位置
脚本所在目录的父目录(间接推导) 如果你在 /project/app/services/ 执行了 python a.py,那 /project 会在路径里
PYTHONPATH 环境变量 你可以自定义模块搜索路径
Python 安装目录(stdlib) 系统级别的模块位置
site-packages 你通过 pip 安装的模块都会在这

几种典型启动方式对 sys.path的影响

启动方式 sys.path[0] 是谁? 特性
python a.py 当前脚本所在目录 ✅ 常用,容易形成隐式路径依赖
python -m mypackage.module 当前工作目录 ✅ 推荐用于包结构执行
import mymodule 当前 sys.path 查找 ✅ 由路径列表控制,推荐使用绝对导入

❌ 相对导入失败(from .utils import x报错)

# 错误示例直接运行
python app/module/a.py

报错
ImportError: attempted relative import with no known parent package

✔️ 正确方式应该用
cd 项目根目录
python -m app.module.a

❌ ModuleNotFoundError: No module named ‘app’

原因:

  • sys.path 里没有 app 所在路径
  • 通常你是在脚本目录下运行了 python xxx.py

• 设置环境变量:

export PYTHONPATH=/your/project/root

• 或在脚本开头加:

import sys
sys.path.append('/your/project/root')

pycharm 导入路径

✅ 本质原因:PyCharm 的“源目录”(Source Root)机制

🧠 什么是 Source Root?

在 PyCharm 中,如果你右键一个目录 → 选择 Mark Directory as → Sources Root,PyCharm 会:

✅ 自动将这个目录

加入解释器的 sys.path

🟡 所以你的 Python 程序在运行时就能直接 import 这个目录下的包

import sys; print('Python %s on %s' % (sys.version, sys.platform))
sys.path.extend(['app','app/test']

当你运行代码时,PyCharm 背后自动执行了上面代码内容,这让 Python 解释器能找到你代码结构中的模块。

如果你在命令行运行代码(比如 python apps/main.py),解释器的 sys.path 并不会包含 JSAI/ 根目录,除非你:

  • 设置了 PYTHONPATH=/Users/edy/Desktop/
  • 或者手动在代码中加上:
import sys, os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
场景 建议
在 PyCharm 中运行 ✅ 设置 Source Root 非常方便
在命令行 / Docker / 服务器中运行 ✅ 显式配置 PYTHONPATH 或使用 python -m …
为团队统一路径导入 ✅ 使用绝对导入 + Source Root 配置一致性

pypi 包导入

✅ 答案核心:PyPI 包不是脚本运行的,而是被import的!并不是运行 pip 安装包中的某个模块脚本,而是以“导入包”方式加载它们

import requests

然后它内部文件结构是这样的:


site-packages/
└── requests/
    ├── __init__.py
    ├── models.py
    └── utils.py

比如在 models.py:

from .utils import some_func  #  相对导入

这是合法的!因为解释器会认为你是以包方式导入的


import requests.models

而不是直接执行 models.py

#  错误示范你不会这么干):
python /site-packages/requests/models.py

🧠 相对导入只有在“模块是某个包中的成员”时才生效。

🧨 如果你直接运行一个含有相对导入的模块(比如 python some_module.py),

解释器认为它是顶层脚本(__main__),此时相对导入会报错

✅ 为什么 pip 安装的库可以这样用?

因为:

  • pip 安装时会把包安装到 site-packages/
  • site-packages/ 自动在 sys.path 中
  • 当你 import some_package 时,Python 会将它识别为“包结构”,内部的 .py 文件可以安心用相对导入

所以这些库在设计时,是假设你通过 import 包名 来用它的,而不是跑它的模块文件 ✅

模块搜索顺序

当你运行 Python 程序时,解释器按照下面这个顺序来搜索模块:

✅sys.path组成顺序(标准机制)

顺序 路径 来源说明
1️⃣ 空字符串 ‘’ 代表 当前脚本所在目录(或当前工作目录)
2️⃣ PYTHONPATH 环境变量指定的路径(如果有) ✅ 用户可配置,优先级高,例如 pycharm 里面的 source-root
3️⃣ site-packages/ pip 安装的第三方包目录
4️⃣ 标准库路径(如 /usr/lib/python3.x) 内置模块,比如 json、math
5️⃣ .pth 文件中注册的路径 如 virtualenv 中 .pth 会添加额外路径

解释器会按照 sys.path 中的顺序进行查找:

  • 一旦找到了某个 模块名.py 或 包目录/(含 init.py),就停止搜索
  • 所以前面的路径优先级更高
类型 是否在 sys.path 优先级高吗? 举例
’’ 当前执行目录 ✅ 默认第一位 脚本本地直接 import
source root (PyCharm) ✅ PyCharm 自动加 高(紧随其后) IDE 推导
PYTHONPATH 设置路径 ✅ 如果你 export 了 PYTHONPATH=/my/app python main.py
site-packages ✅ 默认加入 pip 安装的包
解释器自带路径 最后才查 json, math, asyncio 等

当前工作目录(’’) > PYTHONPATH > source root > site-packages > 标准库路径

init.py 作用

🧠 什么是__init__.py

它是一个特殊的 Python 文件,当你在一个目录下放置 init.py 后:

✅ Python 就会把这个目录当作一个包(package)

功能 说明
✅ 表示该目录是一个包 没有它时,某些 Python 版本/工具无法识别为包(尤其早期)
✅ 控制包的初始化行为 文件会在 import 包时自动执行
✅ 可以用作公共 API 管理 导入子模块、重命名、统一暴露接口
✅ 配合相对导入使用 包内模块可用 .module、..utils 导入
✅ 配合 all 控制 from xxx import * 的行为 显式限制导出的内容

你可以在 utils/init.py 中写:


app/
├── __init__.py
├── utils/
   ├── __init__.py
   ├── a.py
   └── b.py

from .a import func_a
from .b import func_b

__all__ = ["func_a", "func_b"]

“统一导入管理”能力

可以在外部:

from app.utils import func_a, func_b

自 Python 3.3 起,如果你没有 init.py,Python 也能识别包目录(叫 命名空间包)。

✅ 用来组织公共 API

# app/api/__init__.py

from .user import UserApi
from .order import OrderApi

外部只需:

from app.api import UserApi

✅ 初始化日志、环境变量(慎用)

# app/__init__.py

import os
os.environ["APP_MODE"] = "dev"

init.py 就是你显式声明“我这个文件夹是一个 Python 包”的护照,它让你拥有统一导入、初始化控制、模块组织等一整套包管理能力 —— 永远建议保留它 ✅

点赞一下

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦