HOWTO · Python

如何在 Python 中重新启动脚本

了解重新启动或重新运行 Python 脚本的三种方法:使用 os.execv() 替换其进程、使用 subprocess.call() 启动子进程,或在循环中重复应用程序逻辑。

本页内容

要重新启动 Python 脚本,请先选择你需要一个新的解释器进程,还是只需要重复程序的工作。os.execv() 会替换当前进程,subprocess.call() 会启动一个子进程并等待它完成,而循环可以在不重启 Python 的情况下再次运行你的应用程序逻辑。

方法 1:使用 os.execv() 替换当前进程

当当前解释器应被同一脚本的新一次运行替换时,使用 os.execv()。将活动的解释器作为可执行文件传递,并将脚本的参数列表作为其参数传递:

os.execv(sys.executable, [sys.executable, *sys.argv])

导入 os 和 sys,然后在满足重启条件时调用这个表达式。sys.argv 包含脚本路径以及传递给脚本的参数,因此新的解释器会再次收到它们。execv() 成功后不会返回到旧的 Python 代码;操作系统会在同一个进程槽位中运行替换程序。在替换进程之前,请刷新任何需要保留的输出。这个操作依赖于平台,这里没有将其作为已执行的示例展示。

方法 2:使用 subprocess.call() 启动新进程

当应由一个单独的解释器运行脚本、而当前进程仍作为其父进程时,使用 subprocess.call()。调用方式为 subprocess.call([sys.executable, *sys.argv]);先导入 subprocess 和 sys。它会等待子进程完成,然后将子进程的退出状态返回给父进程。父进程不会自动消失,因此如果它应该停止,请在调用后从其重启分支返回或退出。子进程可能继承相同的重启逻辑,因此请确保其条件最终能让这次运行结束。这里没有将这种进程启动行为作为已执行的输出展示。

方法 3:使用循环重复程序逻辑

如果只需要再次运行应用程序的工作,请将该工作放入一个函数中,并从一个受控循环中调用它。这样可以保持同一个解释器,并避免形成一连串子进程。本示例使用一个固定的选项序列来展示两次运行和一次干净的退出;请将该序列替换为你的输入或应用程序条件。

运行此示例:

def run_task(number):
    print(f"Run {number}")

for number, action in enumerate(("restart", "restart", "exit"), start=1):
    if action == "exit":
        print("Done")
        break
    run_task(number)
Run 1
Run 2
Done

在真实的命令行程序中,在每一轮都读取一个操作,并且仅当用户要求重复时才调用 run_task()。为循环设定一个明确的退出条件。重复调用函数不会重置模块全局变量、打开的资源或解释器状态;请关闭或重新初始化下一轮运行不得重用的任何内容。

选择合适的重启方法

使用 os.execv() 替换一次性脚本的进程,在父进程必须等待单独一次运行时使用 subprocess.call(),而当你想重复选定的应用程序工作时使用循环。这些方法的进程生命周期不同:替换不会留下可恢复的旧 Python 帧,子进程运行会在等待期间保持父进程存活,而循环会保留当前解释器的状态。对于 Web 服务,请使用其进程管理器或部署控制,而不是从请求处理程序中重启 worker;在请求中重启可能会中断用户并丢失内存中的状态。

常见问题

我应该选择哪种方法?

对于重复的应用程序操作,选择循环;要替换当前进程,选择 os.execv();当父进程需要等待新的解释器运行时,选择 subprocess.call()。

脚本可以自动重启吗?

可以。像配置变更这样的条件可以在不提示用户的情况下选择重启。请确保该条件能够变为 false,或者以其他方式结束重启循环。

重启会影响性能吗?

启动解释器有启动成本,频繁重启可能会重复初始化工作。循环避免了这种进程启动,但会保留当前解释器的状态,这可能需要显式清理。

我可以在重启时传递更改后的参数吗?

可以。为 os.execv() 或 subprocess.call() 构建一个新的参数列表,而不是原样重用 sys.argv。将解释器可执行文件作为第一项,并在应用程序参数之前包含脚本路径。

Web 应用程序应该从请求中重启其脚本吗?

通常不应该。在请求期间重启 worker 可能会中断服务并丢弃内存中的状态。请使用服务器或进程管理器支持的重启和部署机制。