作为模块导入 (最推荐)
这是最常用、最 Pythonic 的方式,它将另一个脚本(.py 文件)当作一个模块来使用,可以无缝地调用其中的函数、类和变量。
适用场景:
- 你想重用另一个脚本中的代码(函数、类)。
- 你需要将一个大型项目拆分成多个逻辑清晰的文件。
步骤:
-
创建一个模块文件 (
my_module.py),这个文件包含一些可复用的代码。# my_module.py def greet(name): """一个简单的问候函数""" return f"你好, {name}! 欢迎来到 Python 模块的世界。" def add(a, b): """一个加法函数""" return a + b # 模块级别的变量 MODULE_VERSION = "1.0" -
创建主脚本 (
main.py),并导入my_module。# main.py # 1. 导入整个模块 import my_module # 2. 调用模块中的函数 message = my_module.greet("Alice") print(message) sum_result = my_module.add(10, 5) print(f"10 + 5 = {sum_result}") # 3. 访问模块中的变量 print(f"当前模块版本是: {my_module.MODULE_VERSION}") # --- 其他导入方式 --- # 4. 只导入特定的函数/变量,使用时不需要加模块名前缀 from my_module import greet, add print("\n使用 'from ... import ...' 语法:") print(add(100, 200)) # 5. 给模块或函数起一个别名,方便使用或避免命名冲突 import my_module as mm print("\n使用别名:") print(mm.greet("Bob")) from my_module import greet as say_hello print("\n使用函数别名:") print(say_hello("Charlie"))
如何运行:
在终端中,确保 my_module.py 和 main.py 在同一个目录下,然后运行主脚本:
python main.py
输出:
你好, Alice! 欢迎来到 Python 模块的世界。
10 + 5 = 15
当前模块版本是: 1.0
使用 'from ... import ...' 语法:
300
使用别名:
你好, Bob! 欢迎来到 Python 模块的世界。
使用函数别名:
你好, Charlie! 欢迎来到 Python 模块的世界。
使用 subprocess 模块执行脚本
这种方式不是“导入”脚本,而是像在命令行中一样“运行”另一个脚本,它会创建一个新的、独立的 Python 进程。
适用场景:
- 你想将两个脚本完全解耦,它们之间不应该共享内存变量。
- 你想传递命令行参数给另一个脚本。
- 另一个脚本是一个独立的命令行工具,你只是想执行它并获取其输出。
示例:
假设我们有一个脚本 script_to_run.py,它接受命令行参数并打印结果。
# script_to_run.py
import sys
if __name__ == "__main__":
print(f"脚本 {__file__} 开始运行...")
print(f"接收到的参数数量: {len(sys.argv)}")
if len(sys.argv) > 1:
for i, arg in enumerate(sys.argv):
print(f"参数 {i}: {arg}")
else:
print("没有接收到额外参数。")
print("脚本运行结束。")
我们在 main.py 中使用 subprocess 来调用它。
# main.py
import subprocess
import sys
# --- 场景1: 运行脚本,不传递参数 ---
print("--- 场景1: 不带参数运行 ---")
subprocess.run(["python", "script_to_run.py"])
print("\n" + "="*30 + "\n")
# --- 场景2: 运行脚本,并传递参数 ---
print("--- 场景2: 带参数运行 ---")
# 注意:参数需要是一个列表,每个元素都是一个独立的字符串
command = ["python", "script_to_run.py", "arg1", "arg2", "--verbose"]
subprocess.run(command)
print("\n" + "="*30 + "\n")
# --- 场景3: 获取并捕获脚本的输出 ---
print("--- 场景3: 捕获输出 ---")
result = subprocess.run(
["python", "script_to_run.py", "Hello from subprocess"],
capture_output=True, # 捕获标准输出和标准错误
text=True, # 将输出解码为文本
check=True # 如果脚本返回非零退出码,则抛出异常
)
print("脚本执行成功,返回码:", result.returncode)
print("捕获到的标准输出:")
print(result.stdout)
如何运行:
python main.py
输出:
--- 场景1: 不带参数运行 ---
脚本 script_to_run.py 开始运行...
接收到的参数数量: 1
参数 0: script_to_run.py
没有接收到额外参数。
脚本运行结束。
==============================
--- 场景2: 带参数运行 ---
脚本 script_to_run.py 开始运行...
接收到的参数数量: 4
参数 0: script_to_run.py
参数 1: arg1
参数 2: arg2
参数 3: --verbose
脚本运行结束。
==============================
--- 场景3: 捕获输出 ---
脚本 script_to_run.py 开始运行...
接收到的参数数量: 2
参数 0: script_to_run.py
参数 1: Hello from subprocess
脚本运行结束。
脚本执行成功,返回码: 0
捕获到的标准输出:
脚本 script_to_run.py 开始运行...
接收到的参数数量: 2
参数 0: script_to_run.py
参数 1: Hello from subprocess
没有接收到额外参数。
脚本运行结束。
使用 exec() 动态执行代码
exec() 函数会动态地执行一个字符串形式的 Python 代码,它会在当前命名空间中执行,这意味着它会修改主脚本的变量和函数。
⚠️ 警告:exec() 可能存在安全风险,如果执行的代码来自不可信的来源(如用户输入),可能会导致代码注入攻击,请谨慎使用。
适用场景:
- 动态加载和执行插件或配置中定义的代码。
- 在一个受控的环境中快速运行一段代码字符串。
示例:
# main.py
import my_module # 使用方式一中的模块
# --- 场景1: 执行简单的代码字符串 ---
code_string = "print('这是通过 exec() 动态执行的代码')"
print("--- 场景1 ---")
exec(code_string)
# --- 场景2: 执行一个函数定义并调用它 ---
code_function = """
def dynamic_function(x, y):
return x * y
"""
print("\n--- 场景2 ---")
exec(code_function)
# 注意:exec() 定义的函数在当前命名空间中可用
print(f"动态函数的结果: {dynamic_function(7, 8)}")
# --- 场景3: 在指定的命名空间中执行 ---
print("\n--- 场景3 ---")
local_namespace = {}
exec("a = 100", local_namespace)
print(f"局部命名空间中的变量 a: {local_namespace['a']}")
print(f"主脚本中的变量 a: {locals().get('a', 'a 不在主命名空间中')}") # 主脚本中没有 a
# --- 场景4: 执行另一个文件中的所有代码 ---
print("\n--- 场景4 ---")
with open("my_module.py", "r", encoding="utf-8") as f:
module_code = f.read()
exec(module_code)
# 因为是在当前命名空间执行,所以可以直接使用 my_module.py 中定义的函数
print(f"直接调用通过 exec() 加载的 greet 函数: {greet('David')}")
如何运行:
python main.py
输出:
--- 场景1 ---
这是通过 exec() 动态执行的代码
--- 场景2 ---
动态函数的结果: 56
--- 场景3 ---
局部命名空间中的变量 a: 100
主脚本中的变量 a: a 不在主命名空间中
--- 场景4 ---
直接调用通过 exec() 加载的 greet 函数: 你好, David! 欢迎来到 Python 模块的世界。
总结与对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 作为模块导入 | 首选,代码清晰、可复用、共享变量、易于调试。 | 耦合度相对较高,共享内存。 | 绝大多数情况,构建大型应用或库时。 |
subprocess |
完全解耦,可以传递命令行参数,获取脚本输出,不共享内存。 | 进程开销大,通信复杂(需通过文件/网络/管道),调试稍难。 | 需要独立执行、传递参数、或调用现成的命令行工具时。 |
exec() |
高度动态,可以在运行时加载和执行代码。 | 有安全风险,难以调试,可读性差,污染当前命名空间。 | 动态加载插件、执行配置代码等高级、受控场景。 |
简单来说:
- 想用另一个脚本的函数和变量?用
import。 - 想像一个外部命令一样运行另一个脚本?用
subprocess。 - 想在运行时把一段代码字符串变成可执行的代码?用
exec()(但要非常小心)。
