杰瑞科技汇

python调用其他python脚本

核心概念:模块

在 Python 中,任何一个 .py 文件都可以被称为一个 模块,当你在一个脚本中导入另一个脚本时,你实际上是在导入那个脚本所定义的模块。

python调用其他python脚本-图1
(图片来源网络,侵删)

你有一个文件 my_module.py

# my_module.py
def greet(name):
    print(f"Hello, {name}!")
def add(a, b):
    return a + b

这个文件 my_module.py 就是一个模块。


import 语句(最常用、最推荐)

这是最标准、最 Pythonic 的方式,它将整个模块作为一个对象导入。

导入整个模块

使用 import module_name 语法。

python调用其他python脚本-图2
(图片来源网络,侵删)

调用脚本 (main.py):

# main.py
import my_module  # 导入 my_module.py 文件
# 使用模块名.函数名 的方式来调用
my_module.greet("Alice")  # 输出: Hello, Alice!
result = my_module.add(5, 3)
print(f"The sum is: {result}")  # 输出: The sum is: 8

如何运行: 确保 main.pymy_module.py 在同一个目录下,然后在终端运行:

python main.py

优点:

  • 命名空间清晰:所有函数和变量都必须通过 module_name. 前缀来访问,避免了命名冲突。
  • 可读性强:一眼就能看出函数来自哪个模块。
  • 标准做法:这是 Python 社区推荐的最佳实践。

为模块指定别名

如果模块名很长或者为了方便,可以给它起一个短别名。

调用脚本 (main.py):

# main.py
import my_module as mm  # 将 my_module 别名为 mm
# 使用别名来调用
mm.greet("Bob")  # 输出: Hello, Bob!
result = mm.add(10, 20)
print(f"The sum is: {result}")  # 输出: The sum is: 30

优点:

  • 代码更简洁,尤其在多次调用模块内函数时。
  • 常用于导入第三方库,如 import pandas as pd

从模块中导入特定函数/变量

如果你只需要模块中的某一个或几个函数,可以使用 from ... import ... 语法。

调用脚本 (main.py):

# main.py
from my_module import greet, add  # 只导入 greet 和 add 函数
# 直接使用函数名,无需模块名前缀
greet("Charlie")  # 输出: Hello, Charlie!
result = add(100, 200)
print(f"The sum is: {result}")  # 输出: The sum is: 300

优点:

  • 代码更简洁,无需写前缀。
  • 只导入需要的内容,可能略微减少内存占用(虽然现代 Python 解释器优化得很好,这点差别不大)。

⚠️ 注意(缺点):

  • 命名冲突:如果当前脚本中已经有了一个同名的函数,导入后会覆盖它,如果 main.py 里也定义了一个 add 函数,from my_module import add 之后,main.py 里的 add 就会被 my_moduleadd 替换。

导入模块中的所有内容

使用 from ... import * 会导入模块中所有不以下划线 _ 开头的公共名称。

调用脚本 (main.py):

# main.py
from my_module import *  # 导入 my_module 中的所有公共内容
# 直接使用所有导入的函数
greet("David")  # 输出: Hello, David!
print(add(2, 2)) # 输出: 4

⚠️ 强烈不推荐! 这种方式严重破坏命名空间,极易导致难以追踪的命名冲突,是“坏味道”的代码,请尽量避免使用。


exec() 函数(动态执行,需谨慎)

exec() 可以执行一个字符串形式的 Python 代码,你可以用它来读取并执行另一个 .py 文件的内容。

调用脚本 (main.py):

# main.py
with open('my_module.py', 'r', encoding='utf-8') as f:
    script_content = f.read()
作为字符串来执行
exec(script_content)
# 注意:exec执行后,my_module.py中的函数和变量会直接进入当前命名空间
greet("Eve")  # 这可以直接调用,因为exec已经执行了greet的定义
print(add(3, 5)) # 同理

优点:

  • 动态性:可以在运行时决定执行哪个脚本,文件名甚至可以是一个变量。
  • 灵活性高:可以动态修改代码后再执行。

缺点和风险:

  • 安全性极差:如果执行的文件来自不可信的来源(如用户上传),exec() 可能会执行恶意代码,导致安全漏洞。
  • 破坏命名空间:和 from ... import * 类似,它会将执行文件中的所有变量和函数都注入到当前作用域,造成混乱。
  • 调试困难:错误堆栈信息会比较复杂,难以定位。

适用场景: 非常罕见,通常用于动态代码生成或特定领域的脚本引擎。99% 的情况下,你都应该使用 import


subprocess 模块(作为独立进程运行)

这种方式不是在当前 Python 解释器中“导入”模块,而是像在命令行中一样,“调用”另一个 Python 脚本让它作为一个新的、独立的进程运行,主脚本会等待子脚本执行完毕,然后继续执行。

这适用于需要完全隔离环境或捕获子脚本的输出(标准输出/标准错误)的场景。

子脚本 (child_script.py):

# child_script.py
import sys
import time
print("Child script started.")
print(f"Child script received arguments: {sys.argv}") # sys.argv 可以获取命令行参数
for i in range(3):
    print(f"Child is running... {i+1}/3")
    time.sleep(1)
print("Child script finished.")

主脚本 (main.py):

# main.py
import subprocess
import sys
# 准备要传递给子脚本的参数
# 第一个参数是脚本名,后面的是传递给该脚本的参数
args = ['python', 'child_script.py', 'arg1', 'arg2']
print("Main script: about to run the child script.")
# 使用 run() 函数执行命令
# capture_output=True 会捕获子进程的标准输出和标准错误
# text=True 会以文本形式返回输出
# check=True 会在子进程返回非零退出码(即出错)时抛出异常
try:
    result = subprocess.run(args, capture_output=True, text=True, check=True)
    print("\nMain script: child script finished successfully.")
    print("--- Child's STDOUT ---")
    print(result.stdout) # 打印子脚本的输出
    # 如果子脚本有错误输出(print 到 stderr),可以通过 result.stderr 查看
    # print("--- Child's STDERR ---")
    # print(result.stderr)
except subprocess.CalledProcessError as e:
    print(f"Child script failed with error code {e.returncode}")
    print(f"Error output: {e.stderr}")
print("Main script: continuing execution...")

如何运行: 在终端运行:

python main.py

优点:

  • 完全隔离:子脚本在独立的进程中运行,它的内存、变量、错误不会影响主脚本。
  • 可以捕获输入输出:非常适合将其他脚本或命令行工具当作“黑盒”来调用,并获取其结果。
  • 可以传递命令行参数

缺点:

  • 开销大:创建一个新进程比导入模块要慢得多,资源消耗也更大。
  • 通信复杂:如果需要主脚本和子脚本之间进行复杂的数据交换(不只是简单的字符串),会比在同一个进程内导入模块麻烦得多。

总结与最佳实践

方法 语法 优点 缺点 适用场景
import import module
import module as alias
from module import func
Pythonic, 清晰, 安全, 高效 无明显缺点 绝大多数情况下的首选,代码模块化、复用、构建大型应用。
exec() exec(file_content) 动态,灵活 安全风险高,破坏命名空间,难调试 动态代码执行,脚本引擎。应尽量避免
subprocess subprocess.run(['python', 'script.py']) 进程隔离,可捕获I/O,可传参 开销大,通信复杂 调用独立的命令行工具、需要隔离环境、或作为“黑盒”执行外部脚本。

最终建议:

  1. 首选 import:当你需要在一个 Python 程序中使用另一个 Python 文件中的代码时,毫不犹豫地使用 import,这是最正确、最安全、最符合 Python 设计哲学的方式。
  2. 次选 subprocess:当你需要将另一个 Python 脚本(或任何其他命令行程序)作为一个独立的、外部的工具来执行,并获取其输出时,使用 subprocess
  3. 避免 exec():除非你有非常特殊且明确的需求,并且完全理解其风险,否则不要使用 exec() 来执行整个文件。
分享:
扫描分享到社交APP
上一篇
下一篇