在 Python 中调用另一个 Python 脚本或模块,有几种常见的方法,适用于不同的场景,下面我将详细介绍这些方法,并附上代码示例和适用场景。

核心概念:模块 vs. 脚本
在深入方法之前,先理解两个基本概念:
- 模块:一个以
.py结尾的 Python 文件,它包含 Python 代码,可以被其他程序导入和重用。math.py就是一个模块。 - 脚本:一个可执行的 Python 文件,通常包含一系列指令,可以直接从命令行运行来完成一个特定任务,它本身也可以被其他程序导入,但主要设计目的是独立运行。
使用 import (导入模块)
这是最常用、最 Pythonic 的方法,用于在一个文件中导入另一个文件(模块)的代码。
工作原理
当一个 Python 文件(module_a.py)导入另一个文件(module_b.py)时,Python 解释器会执行 module_b.py 中的所有顶层代码(不在函数、类或 if __name__ == "__main__": 块内的代码),并将 module_b.py 中定义的函数、类、变量等加载到当前命名空间中。
代码示例
my_module.py (被调用的模块)

# 定义一个变量
MY_VARIABLE = "Hello from my_module!"
# 定义一个函数
def greet(name):
"""打印一个问候语"""
print(f"Greetings, {name}! This is my_module speaking.")
return f"Hello, {name}"
# 定义一个类
class Greeter:
def __init__(self, message):
self.message = message
def say_hello(self):
print(self.message)
# 顶层代码,当被导入时会自动执行
print("my_module has been imported.")
main_script.py (调用模块的脚本)
# 导入整个模块
import my_module
# 访问模块中的变量
print(f"Accessing variable from my_module: {my_module.MY_VARIABLE}")
# 调用模块中的函数
greeting_message = my_module.greet("Alice")
print(f"Function returned: {greeting_message}")
# 使用模块中的类
greeter_instance = my_module.Greeter("Welcome to the show!")
greeter_instance.say_hello()
# 再次导入模块(Python 会有一个缓存机制,不会重复执行顶层代码)
import my_module
运行 main_script.py 的输出:
my_module has been imported.
Accessing variable from my_module: Hello from my_module!
Greetings, Alice! This is my_module speaking.
Function returned: Hello, Alice
Welcome to the show!
优点
- 代码复用:将常用功能封装成模块,可以在多个项目中重用。
- 命名空间隔离:通过
module.function的方式访问,避免了命名冲突。 - 高效:模块在第一次导入后会被缓存,后续导入不会重新执行代码。
缺点
my_module.py的顶层代码包含大量耗时的操作(如数据库连接、数据加载),这些操作会在导入时立即执行,即使你只需要其中的一两个函数。
使用 if __name__ == "__main__": (保护脚本)
这是 import 方法的完美补充,它让你可以同时将一个文件作为模块导入,也作为独立的脚本运行。
工作原理
当一个 Python 文件被直接运行时,其内置变量 __name__ 的值是 "__main__",当该文件被其他文件作为模块导入时,__name__ 的值是其模块名("my_module")。

将脚本的主要执行逻辑放在 if __name__ == "__main__": 块中,可以确保这些逻辑只在文件被直接运行时执行,而在被导入时被忽略。
代码示例
my_smart_module.py (智能模块/脚本)
# 定义一个变量
MY_VARIABLE = "Hello from my_smart_module!"
# 定义一个函数
def greet(name):
print(f"Greetings, {name}!")
return f"Hello, {name}"
# --- 保护脚本逻辑 ---
# 这部分代码只有在 my_smart_module.py 被直接运行时才会执行
if __name__ == "__main__":
print("This script is being run directly.")
# 调用函数
result = greet("Bob")
print(f"Direct script execution result: {result}")
else:
print(f"my_smart_module is being imported by another script: {__name__}")
main_script.py (调用者)
import my_smart_module
# 当我们导入 my_smart_module 时,其 `if __name__ == "__main__":` 块内的代码不会执行
print("main_script is running.")
# 我们可以正常使用模块中的函数和变量
my_smart_module.greet("Charlie")
运行 main_script.py 的输出:
my_smart_module is being imported by another script: my_smart_module
main_script is running.
Greetings, Charlie!
直接运行 my_smart_module.py 的输出:
This script is being run directly.
Greetings, Bob!
Direct script execution result: Hello, Bob!
优点
- 灵活性:同一个文件既可以作为库(模块)被导入,也可以作为独立的程序运行。
- 最佳实践:这是 Python 开发中的标准做法,使代码更健壮、更易于维护。
使用 subprocess 模块 (作为新进程运行)
如果你需要完全独立地运行另一个 Python 脚本(它有自己的环境、不共享内存、或者你需要捕获它的命令行输出),subprocess 是最佳选择。
工作原理
subprocess 模块允许你创建新的进程,并可以连接到它们的输入/输出/错误管道,从而控制它们,这就像你在终端里输入 python script.py 一样,Python 会启动一个全新的、独立的解释器来执行该脚本。
代码示例
script_to_run.py (被独立调用的脚本)
import sys
import time
print("script_to_run.py started.")
print(f"Arguments received: {sys.argv}")
# 模拟一个耗时任务
for i in range(3):
print(f"Working... ({i+1}/3)")
time.sleep(1)
print("script_to_run.py finished.")
caller_with_subprocess.py (调用者)
import subprocess
import sys
print("Caller script is starting.")
# --- 方法 1: 使用 run() (推荐) ---
# 这会阻塞,直到 script_to_run.py 执行完毕
print("\n--- Running script_with_run ---")
try:
# capture_output=True 会捕获 stdout 和 stderr
# text=True 会将输出解码为文本
result = subprocess.run(
[sys.executable, "script_to_run.py", "arg1", "arg2"],
capture_output=True,
text=True,
check=True # 如果脚本返回非零退出码,会抛出 CalledProcessError
)
print("Script finished successfully.")
print("Stdout:")
print(result.stdout)
print("Stderr:")
print(result.stderr)
print("Return code:", result.returncode)
except subprocess.CalledProcessError as e:
print(f"Script failed with return code {e.returncode}")
print("Stdout:", e.stdout)
print("Stderr:", e.stderr)
# --- 方法 2: 使用 Popen (更灵活,适用于非阻塞调用) ---
print("\n--- Running script_with_popen ---")
# Popen 会立即返回,不会等待子进程结束
process = subprocess.Popen(
[sys.executable, "script_to_run.py"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# 你可以在这里做其他事情,然后再检查子进程的状态
print("Popen started, doing other work...")
# process.communicate() 会等待进程结束并获取输出
stdout, stderr = process.communicate()
print("\nPopen script finished.")
print("Stdout from Popen:")
print(stdout)
print("Stderr from Popen:")
print(stderr)
优点
- 完全隔离:调用者和被调用脚本运行在完全独立的进程中,不会共享内存,安全性高。
- 环境控制:可以为子进程设置独立的环境变量、工作目录等。
- 灵活性:可以轻松传递命令行参数,并捕获标准输出和错误。
缺点
- 开销大:创建新进程比导入模块要慢得多,资源消耗也更高。
- 通信复杂:数据交换需要通过文件、标准输入输出或网络等方式,不如直接导入方便。
总结与选择指南
| 方法 | 核心机制 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
import |
在同一解释器中加载模块代码 | 代码复用、库开发、函数调用 | 高效、简单、共享内存 | 顶层代码会立即执行,可能带来副作用 |
if __name__ == "__main__": |
检查脚本是被直接运行还是被导入 | 脚本开发,使模块可被导入也可独立运行 | 灵活、最佳实践 | 不适用于需要进程隔离的场景 |
subprocess |
启动一个全新的、独立的 Python 进程 | 进程隔离、运行外部程序、捕获命令行输出 | 完全隔离、环境可控、可传参 | 开销大、速度慢、通信复杂 |
如何选择?
- 如果你的目标是重用代码,或者在一个大程序中调用另一个文件中的函数/类:请使用
import,如果被调用的文件是作为脚本设计的,确保它使用了if __name__ == "__main__":来保护其顶层逻辑。 - 如果你想编写一个既可以被导入,也可以直接从命令行运行的脚本:请遵循
if __name__ == "__main__":的模式。 - 如果你需要运行一个独立的、不受当前程序影响的脚本,或者需要捕获其命令行输出:请使用
subprocess,自动化测试、调用部署脚本等。
