Python 执行其他 Python 脚本:从简单调用到高级架构,一篇就够!
Meta描述: 想在 Python 中执行另一个 Python 脚本吗?本文全面详解 subprocess、import、exec 等多种方法,从简单命令行执行到复杂进程间通信,助你掌握 Python 脚本调用精髓,提升代码复用性与项目架构能力。

引言:为什么你需要“Python 执行 Python”?
作为一名 Python 开发者,你是否遇到过以下场景:
- 模块化开发: 将一个庞大的项目拆分为多个功能独立的小脚本,便于维护和测试,然后通过一个主脚本统一调用。
- 任务调度: 希望在当前程序中触发另一个脚本来执行特定的后台任务,如数据处理、文件备份等。
- 动态执行: 需要根据运行时参数,动态加载并执行不同的 Python 代码或脚本文件。
- 隔离环境: 运行一个可能不稳定或存在风险的脚本,希望将其与主程序隔离,避免相互影响。
“Python 执行其他 Python”正是解决这些问题的核心能力,它不仅仅是简单的代码调用,更是构建复杂、灵活、可扩展 Python 应用的基石,我们就来深入探讨实现这一功能的多种方法,并分析它们的适用场景与最佳实践。
import 语句 —— 优雅的内部模块调用
这是最 Pythonic、最基础的方式,当一个 Python 脚本(我们称之为 module_A.py)需要使用另一个脚本(module_B.py)中的函数或类时,可以直接使用 import。
适用场景:

- 将项目拆分为多个模块。
- 调用同一个项目内、共享命名空间的代码。
- 需要共享变量和状态。
核心代码示例:
module_B.py (被调用的模块)
# module_B.py
def greet(name):
"""一个简单的问候函数"""
print(f"Hello, {name} from module_B!")
def calculate(a, b):
"""一个简单的计算函数"""
return a + b
main.py (主调用脚本)
# main.py
import module_B
# 调用 module_B 中的函数
module_B.greet("Alice")
# 调用并获取返回值
result = module_B.calculate(10, 5)
print(f"The result is: {result}")
执行方式: 在命令行中,只需运行主脚本:

python main.py
输出:
Hello, Alice from module_B!
The result is: 15
优缺点分析:
- 优点: 简单直接,天然支持代码复用和模块化,共享内存,性能高。
- 缺点: 被调用的脚本会作为当前进程的一部分运行,无法实现进程隔离。
module_B.py有副作用(如修改了全局状态),会影响主程序。
exec() 函数 —— 动态执行代码字符串
exec() 是一个内置函数,它可以将一个字符串作为 Python 代码来执行。
适用场景:
- 动态执行用户输入的代码(需谨慎,有安全风险)。
- 从配置文件或数据库中加载并执行代码片段。
- 实现简单的脚本语言解释器。
核心代码示例:
# main_exec.py
# 定义一个包含 Python 代码的字符串
code_string = """
def dynamic_function():
print("This function was executed dynamically!")
dynamic_function()
"""
# 使用 exec 执行这段代码
print("--- Executing code from string ---")
exec(code_string)
# 也可以执行一个文件中的代码
with open('module_B.py', 'r') as f:
file_code = f.read()
print("\n--- Executing code from file (module_B.py) ---")
exec(file_code)
# 注意:执行 module_B.py 的代码并不会导入 module_B 这个模块本身
执行方式:
python main_exec.py
输出:
--- Executing code from string ---
This function was executed dynamically!
--- Executing code from file (module_B.py) ---
Hello, world from module_B! # 假设 module_B.py 有一个 print 语句
优缺点分析:
- 优点: 极其灵活,可以动态执行任何有效的 Python 代码。
- 缺点:
- 安全风险极高! 绝对不要执行不可信来源的代码,可能导致代码注入攻击。
- 调试困难,代码可读性差。
- 执行的代码与当前脚本共享全局和局部命名空间,容易产生意外副作用。
subprocess 模块 —— 强大的外部进程管理
当你需要将另一个 Python 脚本作为一个全新的、独立的进程来运行时,subprocess 模块是你的不二之选,它是 os.system() 的现代替代品,功能强大且灵活。
适用场景:
- 需要完全的进程隔离。
- 需要捕获子进程的
stdout、stderr和返回码。 - 需要向子进程传递命令行参数。
- 需要与子进程进行复杂的交互(如管道)。
subprocess 提供了多个函数,我们重点介绍最常用的三个:run(), call(), Popen()。
subprocess.run() (推荐,Python 3.5+)
这是最通用、最推荐的函数,它执行命令并等待其完成。
核心代码示例:
# main_subprocess.py
import subprocess
# --- 场景1:执行一个简单的脚本 ---
print("--- Running a simple script ---")
subprocess.run(['python', 'module_B.py', 'Bob']) # 传递参数
# --- 场景2:执行一个命令并捕获输出 ---
print("\n--- Running a command and capturing output ---")
# 注意:shell=True 可能带来安全风险,如果命令字符串来自用户,请避免使用
result = subprocess.run(['python', '-c', 'print("Hello from subprocess!")'],
capture_output=True, text=True, check=True)
print("Stdout:", result.stdout)
print("Stderr:", result.stderr)
print("Return code:", result.returncode)
执行方式:
python main_subprocess.py
输出:
--- Running a simple script ---
Hello, Bob from module_B! # module_B.py 需要能接收参数,import sys; print(f"Hello, {sys.argv[1]} from module_B!")
--- Running a command and capturing output ---
Hello from subprocess!
Stdout: Hello from subprocess!
Stderr:
Return code: 0
subprocess.Popen() (最灵活,功能最全)
当你需要与子进程进行持续交互,或者在子进程运行时同时做其他事情时,Popen 是最佳选择,它不等待子进程结束。
核心代码示例:
# main_popen.py
import subprocess
import time
print("--- Using Popen for non-blocking execution ---")
# 启动一个长时间运行的脚本
process = subprocess.Popen(['python', 'long_running_script.py'])
print("Main script continues to run...")
time.sleep(2)
print("Main script is doing other work...")
# 等待子进程结束
print("\nWaiting for the subprocess to complete...")
process.wait()
print(f"Subprocess finished with return code: {process.returncode}")
(假设 long_running_script.py 中有 time.sleep(5) 和一些打印语句)
优缺点分析:
- 优点:
- 完全的进程隔离,安全性高(相比
exec)。 - 功能强大,可以控制输入、输出、错误流,支持管道。
- 能够获取子进程的详细状态信息。
- 完全的进程隔离,安全性高(相比
- 缺点:
- 相比
import,进程创建和通信有额外的开销。 - API 相对复杂,
Popen尤其需要仔细管理。
- 相比
multiprocessing 模块 —— 并行计算的利器
如果你的目标是利用多核 CPU 并行执行任务,multiprocessing 是最专业的选择,它创建进程池,每个进程都可以独立运行一个 Python 脚本或函数。
适用场景:
- CPU 密集型任务,如图像处理、科学计算。
- 需要并行处理多个独立的任务。
核心代码示例:
# main_multiprocessing.py
import multiprocessing
import time
def worker_task(script_name, task_id):
"""这个函数将在每个子进程中运行"""
print(f"Process {multiprocessing.current_process().name} is running task {task_id} by executing {script_name}")
# 模拟工作
time.sleep(2)
print(f"Process {multiprocessing.current_process().name} finished task {task_id}")
return f"Result from task {task_id}"
if __name__ == '__main__':
print("--- Using a Pool of processes ---")
# 创建一个进程池
with multiprocessing.Pool(processes=2) as pool:
# 准备任务参数
tasks = [('module_B.py', 1), ('module_B.py', 2)]
# 使用 apply_async 异步执行任务
results = [pool.apply_async(worker_task, t) for t in tasks]
# 获取结果(会等待任务完成)
for res in results:
print("Got result:", res.get())
print("All tasks in the pool are done.")
优缺点分析:
- 优点:
- 真正的并行执行,绕过 GIL(全局解释器锁),充分利用多核 CPU。
- 专为并行计算设计,提供了进程池、队列等高级工具。
- 缺点:
- 主要用于并行计算,对于简单的脚本调用可能“杀鸡用牛刀”。
- 进程间通信比线程间通信复杂,通常需要使用
Queue或Pipe。
方法对比与选型指南
| 方法 | 进程隔离 | 主要用途 | 复杂度 | 安全性 | 性能 |
|---|---|---|---|---|---|
import |
否 | 内部模块化调用 | 低 | 高 | 最高 |
exec() |
否 | 动态执行代码 | 中 | 极低 | 高 |
subprocess |
是 | 外部脚本/命令执行 | 中-高 | 高 | 中 |
multiprocessing |
是 | 并行计算/任务 | 高 | 高 | 中(但总吞吐量高) |
如何选择?问自己三个问题:
-
我需要隔离吗?
- 不需要: 优先选择
import,这是最干净、最高效的方式。 - 需要: 排除
import和exec。
- 不需要: 优先选择
-
我需要并行吗?
- 是: 选择
multiprocessing。 - 否: 继续下一个问题。
- 是: 选择
-
我需要交互/捕获输出吗?
- 是: 选择
subprocess,特别是run()或Popen()。 - 否: 只需让它运行,
subprocess.run()或subprocess.call()都可以。
- 是: 选择
高级技巧与最佳实践
-
路径处理: 使用
os.path或pathlib模块来构建脚本路径,确保你的代码在任何环境下都能找到目标脚本。import os script_path = os.path.join('scripts', 'module_B.py') subprocess.run(['python', script_path]) -
参数传递: 使用
shlex.quote()来处理包含空格或特殊字符的参数,防止命令注入。import shlex arg = "my file.txt" safe_arg = shlex.quote(arg) subprocess.run(['python', 'script.py', safe_arg])
-
超时控制: 使用
subprocess.run(..., timeout=10)来防止子进程无限期挂起。 -
日志记录: 在被调用的脚本中实现良好的日志记录,而不是仅仅依赖
print,这样你可以通过重定向stdout和stderr来集中管理日志。
“Python 执行其他 Python”是一项核心技能,它为我们的编程工具箱增添了强大的灵活性,从简单的 import 到复杂的 subprocess 和 multiprocessing,每种方法都有其独特的价值。
import是模块化开发的基石。exec是一把锋利的双刃剑,适用于特定动态场景,但必须警惕安全风险。subprocess是连接外部世界的桥梁,是实现进程隔离和命令行交互的利器。multiprocessing是解锁并行计算潜能的钥匙。
掌握这些方法,并根据你的具体需求——无论是代码复用、进程隔离还是并行加速——做出明智的选择,你将能够构建出更加健壮、高效和可维护的 Python 应用,希望本文能为你提供清晰的指引和实用的代码示例,助你在 Python 编程的道路上更进一步。
