杰瑞科技汇

Python如何导入其他文件的Python模块?

模块

在 Python 中,一个 .py 文件就是一个模块,当你导入一个文件时,你实际上是在导入这个文件所定义的模块。

Python如何导入其他文件的Python模块?-图1
(图片来源网络,侵删)

假设你有以下两个文件,它们都在同一个目录下:

my_module.py (要被导入的文件)

# 定义一个变量
MY_CONSTANT = "Hello from my_module!"
# 定义一个函数
def greet(name):
    """一个简单的问候函数"""
    return f"Hello, {name}! Welcome to the module."
# 定义一个类
class Calculator:
    """一个简单的计算器类"""
    def add(self, a, b):
        return a + b

main.py (执行导入的文件)

# --- 导入方式演示 ---
# 方式一:导入整个模块
import my_module
# 使用模块名.成员名 的方式访问
print("--- 使用 'import my_module' ---")
print(my_module.MY_CONSTANT)
print(my_module.greet("Alice"))
calc = my_module.Calculator()
print(f"10 + 5 = {calc.add(10, 5)}")
print("\n" + "="*30 + "\n")
# 方式二:导入特定成员
from my_module import greet, MY_CONSTANT
# 直接使用成员名,无需模块名前缀
print("--- 使用 'from my_module import greet, MY_CONSTANT' ---")
print(MY_CONSTANT)
print(greet("Bob"))
# 注意:Calculator 类没有被导入,所以下面这行会报错
# calc = Calculator() 

当你运行 main.py 时,输出将会是:

Python如何导入其他文件的Python模块?-图2
(图片来源网络,侵删)
--- 使用 'import my_module' ---
Hello from my_module!
Hello, Alice! Welcome to the module.
10 + 5 = 15
==============================
--- 使用 'from my_module import greet, MY_CONSTANT' ---
Hello from my_module!
Hello, Bob!

详细的导入方法

import module_name

这是最基本、最推荐的方式,尤其是在大型项目中。

  • 如何工作:它将整个 my_module.py 文件加载到内存中,并将其作为一个名为 my_module 的命名空间。
  • 如何访问:必须使用 模块名.成员名 的格式来访问模块中的变量、函数和类。
  • 优点
    • 命名空间清晰:可以清楚地知道一个函数或变量来自哪个模块,避免了命名冲突,如果另一个模块里也有一个 greet 函数,使用 my_module.greet()other_module.greet() 就能明确区分。
    • 代码可读性强:代码的来源一目了然。
  • 缺点

    每次访问成员时都需要输入模块名,稍微繁琐一点。

from module_name import name1, name2, ...

这种方式可以导入模块中的一个或多个特定成员。

  • 如何工作:它将指定的成员(如 greet, MY_CONSTANT)直接导入到当前文件的命名空间中。
  • 如何访问:直接使用成员名,如 greet("Bob")
  • 优点
    • 代码简洁:无需每次都写模块名,对于频繁使用的成员非常方便。
  • 缺点
    • 可能引发命名冲突:如果当前文件已经有一个同名的变量或函数,导入的成员会覆盖它。
    • 可读性稍差:如果不看文件顶部的 import 语句,可能不清楚 greet 函数是从哪里来的。

from module_name import *

这是一个强烈不推荐的用法,它会导入模块中除下划线 _ 开头之外的所有成员。

Python如何导入其他文件的Python模块?-图3
(图片来源网络,侵删)
  • 如何工作:将目标模块的所有公共成员全部导入到当前命名空间。
  • 如何访问:直接使用成员名。
  • 优点:无(除了在交互式解释器中快速测试)。
  • 缺点
    • 严重污染命名空间:几乎肯定会导致命名冲突,尤其是在多个模块都使用 import * 的情况下。
    • 代码难以理解和维护:你无法一眼看出一个变量或函数的来源,调试起来非常痛苦。
    • 破坏了显式优于隐式的 Python 哲学

import module_name as alias

为模块或成员提供一个简短的别名,这在处理长模块名或避免命名冲突时非常有用。

  • 为整个模块设置别名

    import numpy as np # NumPy 库的常用别名
    import pandas as pd # Pandas 库的常用别名
    array = np.array([1, 2, 3])
    data_frame = pd.DataFrame({'col1': [1, 2]})
  • 为成员设置别名

    from my_module import greet as say_hello
    print(say_hello("Charlie")) # 输出: Hello, Charlie! Welcome to the module.

Python 的模块搜索路径

当你执行 import my_module 时,Python 会在以下位置按顺序查找 my_module.py 文件:

  1. 当前目录:Python 会在你运行脚本的当前目录下查找。
  2. PYTHONPATH 环境变量:这是一个环境变量,包含了一系列目录,Python 也会在这些目录中查找。
  3. 标准库路径:Python 安装目录下的 lib 等文件夹,包含了所有内置的标准库模块(如 os, sys, math)。
  4. 第三方库路径:通常是 site-packages 目录,你通过 pip 安装的所有第三方库都在这里。

你可以通过 sys.path 列表来查看 Python 的所有搜索路径:

import sys
print(sys.path)

这个列表是可修改的,如果你想让 Python 去一个特定的目录找模块,可以这样做:

import sys
# 添加一个自定义路径到搜索列表的开头
sys.path.insert(0, '/path/to/your/modules') 

注意:直接修改 sys.path 是一种快速但不太规范的方法,更专业的做法是使用虚拟环境(见下文)。


当你的项目变得很大时,模块也会越来越多,为了更好地组织模块,Python 引入了的概念。

  • :本质上是一个包含 __init__.py 文件的目录,这个文件可以是一个空文件,它的作用是告诉 Python:“这个目录是一个包,可以被导入”。
  • 子包:包内可以再包含子包,形成层级结构。

目录结构示例:

my_project/
├── main.py
├── my_package/
│   ├── __init__.py      # 包的初始化文件
│   ├── module_a.py      # 模块 A
│   ├── module_b.py      # 模块 B
│   └── sub_package/
│       ├── __init__.py  # 子包的初始化文件
│       └── module_c.py  # 模块 C

如何导入包中的模块?

假设上面的目录结构,在 main.py 中:

  1. 导入包中的模块

    # 从 my_package 包中导入 module_a
    from my_package import module_a
    module_a.some_function()
  2. 导入模块中的特定成员

    # 从 my_package.sub_package 中导入 module_c 的一个函数
    from my_package.sub_package.module_c import some_c_function
    some_c_function()
  3. 使用 import 导入子包模块

    import my_package.sub_package.module_c as c_module
    c_module.some_c_function()

最佳实践与常见问题

使用虚拟环境

这是最重要的一点!当你开始一个新项目时,永远不要直接在系统 Python 环境中安装库,使用虚拟环境可以为每个项目创建一个隔离的、干净的 Python 环境。

  • 为什么?

    • 避免版本冲突:项目 A 需要 requests==2.25.0,而项目 B 需要 requests==2.28.0,虚拟环境可以完美解决这个问题。
    • 保持系统 Python 干净:不会把各种项目的库搞乱系统环境。
    • 可复现性:可以轻松地创建一个包含所有依赖项的环境,方便部署和分享。
  • 如何使用?

    # 1. 创建虚拟环境 (venv 是 Python 内置的)
    python -m venv my_project_env
    # 2. 激活虚拟环境
    # Windows:
    my_project_env\Scripts\activate
    # macOS / Linux:
    source my_project_env/bin/activate
    # 3. 现在你看到命令行前面会有 (my_project_env) 字样
    # 安装库
    pip install numpy pandas
    # 4. 退出虚拟环境
    deactivate

在虚拟环境中,你只需要将你的项目根目录(包含 my_package 的目录)作为工作目录,Python 就能正确找到你的模块。

循环导入

这是模块导入中一个非常经典的错误。

场景: module_a.py 依赖于 module_b.pymodule_b.py 也依赖于 module_a.py

# module_a.py
print("Module A is being imported...")
from module_b import my_function_b  # 尝试导入 B 的函数
def my_function_a():
    print("Function A is called")
    my_function_b() # 调用 B 的函数
# module_b.py
print("Module B is being imported...")
from module_a import my_function_a # 尝试导入 A 的函数
def my_function_b():
    print("Function B is called")
    my_function_a() # 调用 A 的函数
# main.py
import module_a

运行 main.py 时,你会得到一个 ImportError,因为 Python 在加载 module_a 时,会尝试加载 module_b,而 module_b 又会尝试加载 module_a,形成一个死循环。

解决方案:

  • 重构代码:这是最好的方法,尝试将两个模块相互依赖的代码提取到一个新的、共同的第三个模块中。

  • 延迟导入:在一个函数内部进行导入,而不是在模块顶层。

    # module_a.py
    def my_function_a():
        # 在函数内部才导入 B
        from module_b import my_function_b
        print("Function A is called")
        my_function_b()
    # module_b.py
    def my_function_b():
        # 在函数内部才导入 A
        from module_a import my_function_a
        print("Function B is called")
        my_function_a()

    这种方法可以打破循环,但可能会使代码逻辑变得不那么清晰,应谨慎使用。

导入方式 语法 优点 缺点 适用场景
标准导入 import module 命名空间清晰,无冲突 代码稍长 强烈推荐,尤其是大型项目
特定导入 from module import name 代码简洁 可能引发命名冲突 频繁使用模块中的少量成员时
通配导入 from module import * 严重污染命名空间,不推荐 极少使用,仅在交互式解释器中快速测试
别名导入 import module as alias 避免冲突,代码简洁 可读性取决于别名 长模块名或库的惯用别名(如 np, pd

显式优于隐式清晰优于简洁,在大多数情况下,优先使用 import modulefrom module import specific_name,并善用虚拟环境来管理你的项目依赖。

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