Python 3 脚本不直接执行 Python 2 代码,而是像操作系统一样,启动一个新的、独立的进程去运行那个 Python 2 脚本。

Python 3 的 subprocess 模块是实现这一目标的最佳工具,下面我将从简单到复杂,详细介绍几种方法,并讨论它们的优缺点和适用场景。
准备工作:创建一个 Python 2 脚本
为了演示,我们先创建一个简单的 Python 2 脚本,py2_script.py。
py2_script.py
# -*- coding: utf-8 -*-
import sys
print "--- Python 2 Script Started ---"
print "Python version:", sys.version
print "Arguments received:", sys.argv
# 模拟一个耗时任务
import time
time.sleep(2)
# 从命令行参数中获取一个名字,并打印问候语
if len(sys.argv) > 1:
name = sys.argv[1]
print "Hello, %s! (from Python 2)" % name
else:
print "Hello, World! (from Python 2)"
print "--- Python 2 Script Finished ---"
我们将在 Python 3 脚本中调用这个 py2_script.py。

使用 subprocess.run() (推荐,Python 3.5+)
subprocess.run() 是现代 Python (3.5+) 中推荐的、最简单、最安全的方式,它提供了一个统一的接口来运行命令并等待其完成。
基本调用(无参数)
py3_main.py
import subprocess
# 1. 定义要执行的命令
# ['python2', 'py2_script.py'] 是一个列表,表示要运行的命令及其参数
# 这比使用字符串 "python2 py2_script.py" 更安全,可以避免shell注入攻击
command = ['python2', 'py2_script.py']
# 2. 运行命令
# check=True: 如果进程返回非零退出码(表示出错),会抛出 CalledProcessError 异常
# capture_output=True: 捕获标准输出和标准错误
# text=True: 将输出解码为文本(而不是字节),默认使用系统编码
try:
print("--- Python 3: Calling Python 2 script ---")
result = subprocess.run(command, check=True, capture_output=True, text=True, encoding='utf-8')
# 3. 处理结果
print("\n--- Python 2 Script Output ---")
print(result.stdout) # 打印标准输出
print("\n--- Python 3: Call Successful ---")
print("Return code:", result.returncode)
except subprocess.CalledProcessError as e:
print("\n--- Python 3: Error occurred ---")
print("Return code:", e.returncode)
print("Error output:", e.stderr)
运行结果:
--- Python 3: Calling Python 2 script ---
--- Python 2 Script Output ---
--- Python 2 Script Started ---
Python version: 2.7.18 (default, Oct 21 2025, 16:48:29)
[GCC 10.2.1 20250110]
Arguments received: ['py2_script.py']
Hello, World! (from Python 2)
--- Python 2 Script Finished ---
--- Python 3: Call Successful ---
Return code: 0
传递参数
调用时,只需将参数添加到命令列表中即可。

py3_main_with_args.py
import subprocess
command = ['python2', 'py2_script.py', 'Alice']
# 注意:参数 'Alice' 直接作为列表的元素添加
try:
print("--- Python 3: Calling Python 2 script with arguments ---")
result = subprocess.run(command, check=True, capture_output=True, text=True, encoding='utf-8')
print("\n--- Python 2 Script Output ---")
print(result.stdout)
except subprocess.CalledProcessError as e:
print("\n--- Python 3: Error occurred ---")
print("Error output:", e.stderr)
运行结果:
--- Python 3: Calling Python 2 script with arguments ---
--- Python 2 Script Output ---
--- Python 2 Script Started ---
Python version: 2.7.18 (default, Oct 21 2025, 16:48:29)
[GCC 10.2.1 20250110]
Arguments received: ['py2_script.py', 'Alice']
Hello, Alice! (from Python 2)
--- Python 2 Script Finished ---
使用 subprocess.Popen() (更灵活,适用于复杂交互)
Popen (process open) 是一个更底层的接口,它不会等待子进程结束,而是立即返回一个 Popen 对象,这对于需要与子进程进行持续交互(发送输入、实时读取输出)或并发运行多个进程的场景非常有用。
示例:运行并等待,获取输出
py3_main_popen.py
import subprocess
import time
command = ['python2', 'py2_script.py', 'Bob']
print("--- Python 3: Starting Python 2 script with Popen ---")
# Popen 不会等待,立即返回一个 Popen 对象
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8')
# 你可以在这里做其他事情,而不是立即等待
print("Python 3 script is doing other work...")
time.sleep(1)
# 使用 communicate() 方法来等待进程结束,并获取所有输出
# communicate() 会正确处理管道,避免死锁
print("\nPython 3 is now waiting for the Python 2 script to finish...")
stdout, stderr = process.communicate()
if process.returncode == 0:
print("\n--- Python 2 Script Output ---")
print(stdout)
else:
print("\n--- Python 3: Error occurred ---")
print("Return code:", process.returncode)
print("Error output:", stderr)
示例:实时读取输出
这是一个 Popen 的强大功能,可以逐行或实时地获取子进程的输出。
py3_main_popen_realtime.py
import subprocess
import time
command = ['python2', 'py2_script.py', 'Charlie']
print("--- Python 3: Starting Python 2 script with Popen (real-time output) ---")
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8')
# 将 stdout 和 stderr合并,然后逐行读取
# iter(process.stdout.readline, '') 会一直读取直到遇到空字符串(进程结束)
for line in iter(process.stdout.readline, ''):
print("PY2 > ", line.strip()) # .strip() 去除末尾的换行符
# 确保进程已经结束
process.wait()
print("\n--- Python 3: Python 2 script finished with return code", process.returncode, "---")
使用 os.system() (简单但过时)
os.system() 是最简单的方法,但它功能有限,不推荐在新代码中使用,它只是简单地执行命令,并将输出直接打印到你的控制台,你无法捕获输出或检查返回码(除非手动检查)。
py3_main_os_system.py
import os
command = "python2 py2_script.py David"
print("--- Python 3: Calling os.system() ---")
# 返回码是 shell 的返回码,而不是 Python 2 脚本的直接返回码
return_code = os.system(command)
print("\n--- Python 3: os.system() finished with return code", return_code, "---")
为什么不推荐?
- 无法捕获输出:所有输出都直接显示在终端。
- 安全性差:如果命令来自用户输入,容易受到 shell 注入攻击。
- 功能有限:无法进行复杂的交互或错误处理。
关键注意事项和最佳实践
-
如何指定
python2可执行文件?-
直接使用
'python2'假设它在你的系统PATH环境变量中,这是最常见的情况。 -
python2不在PATH中,或者你想使用特定的版本,请提供其完整路径:# Linux/macOS command = ['/usr/bin/python2', 'py2_script.py'] # Windows command = ['C:\\Python27\\python.exe', 'py2_script.py']
-
-
数据交换(进程间通信)
- 命令行参数:适合传递简单的、少量的数据(如文件名、用户名),数据量有限,且需要小心处理特殊字符(如空格、引号)。
- 标准输入/输出/错误:
subprocess模块通过stdin,stdout,stderr参数完美支持。process.communicate()是处理输入输出的安全方式。 - 文件:最可靠、最简单的方式,让 Python 2 脚本将结果写入一个文件,Python 3 脚本再从该文件中读取,对于大数据量尤其适用。
- 网络套接字:如果两个脚本在同一台机器上运行,可以让它们监听和连接本地端口进行通信。
- 临时文件:使用
tempfile模块创建临时文件进行数据交换,并在使用后自动删除。
-
编码问题
- Python 2 和 Python 3 在字符串处理上有根本不同,Python 2 的
str是字节,而 Python 3 的str是 Unicode。 - 当使用
subprocess时,务必通过text=True(或universal_newlines=True) 和encoding='utf-8'参数来明确指定文本的编码,这样可以确保字符串在两个进程之间正确传递和转换,避免UnicodeDecodeError或UnicodeEncodeError。
- Python 2 和 Python 3 在字符串处理上有根本不同,Python 2 的
-
错误处理
- 始终检查子进程的返回码。
subprocess.run的check=True会自动帮你检查。 - 捕获
subprocess.CalledProcessError异常来处理执行失败的情况。 - 捕获
FileNotFoundError异常,以防python2命令或脚本文件不存在。
- 始终检查子进程的返回码。
| 方法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
subprocess.run() |
简单、安全、推荐、功能全面、处理错误和输出方便 | 需要 Python 3.5+ | 绝大多数情况下的首选。 |
subprocess.Popen() |
非常灵活、可并发、可实时交互、可进行复杂进程控制 | 代码稍复杂,需要手动管理进程 | 需要与子进程持续交互、并发运行、或获取实时输出时。 |
os.system() |
极其简单,一行代码即可 | 功能弱、不安全、无法捕获输出、不推荐用于生产 | 快速测试、一次性脚本,不关心输出和错误时。 |
对于在 Python 3 中调用 Python 2 进程,强烈建议你从 subprocess.run() 开始,它提供了最佳的性能、安全性和易用性组合,如果发现 run() 无法满足你的需求(例如需要实时输出),再考虑使用 Popen。
