杰瑞科技汇

Python如何import自定义模块?

模块和包

在 Python 中,代码组织的方式主要有两种:

Python如何import自定义模块?-图1
(图片来源网络,侵删)
  1. 模块:一个 .py 文件,就是一个模块,文件名就是模块名(去掉 .py 后缀)。my_module.py 就是一个名为 my_module 的模块。
  2. :一个包含 __init__.py 文件的目录,包用于组织多个相关的模块。__init__.py 文件可以是一个空文件,它的存在告诉 Python 这个目录应该被当作一个包来处理。

导入同一目录下的模块

这是最简单的情况,假设你有以下文件结构:

my_project/
├── main.py
└── my_module.py

my_module.py 的内容:

# my_module.py
def greet(name):
    """一个简单的问候函数"""
    return f"Hello, {name} from my_module!"
PI = 3.14159

main.py 的内容:

# main.py
# 直接导入模块名
import my_module
# 使用模块名.函数名/变量名 来访问
message = my_module.greet("Alice")
print(message)
# 访问模块中的变量
print(f"The value of PI is: {my_module.PI}")

如何运行: 在终端中,进入到 my_project 目录,然后运行:

Python如何import自定义模块?-图2
(图片来源网络,侵删)
python main.py

输出:

Hello, Alice from my_module!
The value of PI is: 3.14159

说明: 当 Python 执行 import my_module 时,它会去当前执行的脚本所在的目录(这里是 my_project)寻找名为 my_module.py 的文件,找到后,就会执行它并将其内容加载到 my_module 这个命名空间中。


导入子目录下的模块(包)

当你的项目变大,需要将模块放入子目录时,你就需要创建一个

假设文件结构如下:

Python如何import自定义模块?-图3
(图片来源网络,侵删)
my_project/
├── main.py
└── utils/
    ├── __init__.py  # 必须存在,可以为空
    └── string_utils.py

utils/string_utils.py 的内容:

# utils/string_utils.py
def reverse_string(text):
    """反转一个字符串"""
    return text[::-1]

utils/__init__.py 的内容: (这个文件可以为空,但强烈建议保留它,即使只写一行注释 # utils package

main.py 的内容:

现在你有两种主要的导入方式:

方式 A:导入整个包,然后使用 包.模块 的方式

# main.py
# 导入 utils 包
import utils
# 然后通过 utils.模块名 来访问模块中的内容
reversed_text = utils.string_utils.reverse_string("Python")
print(reversed_text)

方式 B:从包中导入特定的模块(更常用)

# main.py
# 从 utils 包中导入 string_utils 模块
from utils import string_utils
# 直接使用模块名.函数名
reversed_text = string_utils.reverse_string("Python")
print(reversed_text)

方式 C:从模块中导入特定的函数/变量

# main.py
# 从 utils.string_utils 模块中直接导入 reverse_string 函数
from utils.string_utils import reverse_string
# 直接使用函数名,非常方便
reversed_text = reverse_string("Python")
print(reversed_text)

如何运行: 同样,在 my_project 目录下运行 python main.py,以上三种方式都能正常工作并输出 nohtyP


导入上级目录或其他目录的模块

这是最常见也最容易让人困惑的情况,Python 默认只会从当前工作目录和标准库路径中查找模块,如果你想导入不在这些位置的模块,你需要修改 Python 的模块搜索路径。

假设文件结构如下:

project_root/
├── main.py
├── src/
│   └── my_module.py
└── tests/
    └── test_main.py

目标:tests/test_main.py 中,想导入 src/my_module.py

方法 1:修改 sys.path(最直接,但不推荐用于生产环境)

在需要导入的脚本中,动态地将上级目录(或目标目录)添加到 sys.path 列表中。

tests/test_main.py 的内容:

# tests/test_main.py
import sys
import os
# 获取当前文件所在的目录 (tests)
current_dir = os.path.dirname(os.path.abspath(__file__))
# 获取上级目录 (project_root)
parent_dir = os.path.dirname(current_dir)
# 将上级目录添加到 sys.path 的最前面
sys.path.insert(0, parent_dir)
# 现在就可以像在 main.py 中一样直接导入了
import src.my_module as my_module
# 或者 from src import my_module
# 或者 from src.my_module import greet
message = my_module.greet("Tester")
print(message)

说明:

  • os.path.abspath(__file__) 获取 test_main.py 的绝对路径。
  • os.path.dirname() 获取路径的目录部分。
  • sys.path.insert(0, ...) 将路径插入到 sys.path 的开头,确保 Python 优先搜索这个路径。

优点:简单直接,不需要安装任何东西。 缺点:修改了全局的 sys.path,可能会影响其他模块的导入,并且只在当前脚本运行时有效,不推荐在大型项目或库的代码中使用。

方法 2:设置 PYTHONPATH 环境变量(推荐)

PYTHONPATH 是一个环境变量,它的作用是告诉 Python 解释器除了默认路径外,还应该去哪些目录里寻找模块。

如何设置(以 Linux/macOS 为例): 在终端中,运行以下命令(临时生效,关闭终端后失效):

# export PYTHONPATH=$PYTHONPATH:/path/to/your/project_root
# 假设 project_root 在 /home/user/my_project
export PYTHONPATH=$PYTHONPATH:/home/user/my_project
# 然后运行你的测试脚本
python tests/test_main.py

如何设置(以 Windows 为例): 在命令提示符中:

# set PYTHONPATH=%PYTHONPATH%;C:\path\to\your\project_root
# 假设 project_root 在 C:\Users\YourUser\my_project
set PYTHONPATH=%PYTHONPATH%;C:\Users\YourUser\my_project
# 然后运行你的测试脚本
python tests\test_main.py

test_main.py 中,你就可以直接导入了,无需修改 sys.path

# tests/test_main.py
# 因为 project_root 已经在 PYTHONPATH 中了
import src.my_module as my_module
message = my_module.greet("Tester from PYTHONPATH")
print(message)

优点:比修改 sys.path 更干净,是配置开发环境的常用方法。 缺点:需要手动设置环境变量。

方法 3:使用相对导入(仅用于包内部)

重要:相对导入只能在包的内部模块中使用,不能在最顶层的脚本(如 main.py)中使用。

假设你的项目结构如下,并且你希望将 main.py 也变成一个模块,放在 src 包里:

project_root/
├── src/
│   ├── __init__.py
│   ├── main.py      # 作为 src 包的一部分
│   └── my_module.py
└── tests/
    └── test_main.py

你想在 src/main.py 中导入 src/my_module.py

src/main.py 的内容:

# src/main.py
# 使用点号 . 表示当前包
# .my_module 表示 src 包下的 my_module 模块
from . import my_module
# 或者 from .my_module import greet
message = my_module.greet("Main Script")
print(message)

如何运行: 你不能直接用 python src/main.py 来运行,因为相对导入的上下文是包,而不是脚本,你需要使用 -m 标志来告诉 Python 将一个模块作为脚本来运行。

# 从 project_root 目录运行
python -m src.main

说明:

  • 代表当前包。
  • 代表上一级包。
  • 代表上上级包,以此类推。

优点:非常清晰,明确指出了模块间的相对关系。 缺点:使用方式有特殊要求(-m),不能直接作为脚本运行。


总结与最佳实践

场景 文件结构 推荐方法 示例
同一目录 main.py, my_module.py import my_module import my_module
子目录(包) main.py, utils/mod.py from utils import mod from utils import string_utils
上级目录 main.py, src/mod.py 设置 PYTHONPATH 在终端设置 export PYTHONPATH=...
包内部 pkg/main.py, pkg/mod.py 使用相对导入 from . import mod (运行时用 python -m pkg.main)

核心要点:

  1. 模块就是 .py 文件,包是带 __init__.py 的目录。
  2. import module 会加载整个模块,通过 module.func 访问。
  3. from module import func 只加载指定的函数/变量,可以直接使用 func
  4. 跨目录导入的核心是让 Python 能找到你的文件,最灵活和标准的方式是设置 PYTHONPATH
  5. 避免在代码中写死路径修改 sys.path,除非是临时的调试脚本。
  6. 相对导入 (from . import ...) 是包内部通信的优雅方式,但要记得用 python -m package.module 来运行。

希望这个详细的解释能帮助你完全理解 Python 的自定义导入机制!

分享:
扫描分享到社交APP
上一篇
下一篇