【问题标题】:What will happen if I modify a Python script while it's running?如果我在 Python 脚本运行时修改它会发生什么?
【发布时间】:2011-03-14 09:47:05
【问题描述】:

想象一个python脚本需要很长时间才能运行,如果我在运行时修改它会发生什么?结果会不会不一样?

【问题讨论】:

  • 程序被加载到你的主内存中。如果您更改源文件,则不会发生任何事情。想象一下 CPU 会从硬盘读取指令...
  • @Felix:这就是所谓的“就地执行”(XIP)。
  • 你可以动态重新加载模块的代码,见stackoverflow.com/questions/437589/…
  • 请注意,Windows 批处理文件 确实 会在原地执行,所以这不是一个假设的问题,有些语言会以这种方式运行。
  • 如果我有多个带有源代码的 .py 文件,并且程序在运行时调用这些不同的文件/类,是否仍然如此?还在用原来的版本吗?如果我的程序不完全适合 RAM 怎么办?

标签: python


【解决方案1】:

没什么,因为 Python 会将您的脚本预编译成 PYC 文件并启动它。

但是,如果发生某种异常,您可能会得到一个稍有误导性的解释,因为行 X 的代码可能与您启动脚本之前的代码不同。

【讨论】:

  • 不一定是文件。
  • 但是如果你在运行时重新启动,当新的pyc文件覆盖旧的时,会不会导致已经运行的程序出现问题?
  • 什么也没发生。我也在一个小测试中检查了它。会发生什么:pyc 只是编译。这个编译被加载到 RAM 中然后执行。所以总是可以改变程序,重新编译和运行另一个实例,例如在不同的控制台中。
  • @Chris 我在一个控制台中启动了我的 python 脚本实例。在运行时,我更改了两行代码并在单独的控制台中启动了另一个实例。过了一会儿,我从第一个控制台返回了一个关于我在启动它后更改的两行代码的错误!请帮助
  • @Chris 我想我知道这里发生了什么。如果您在运行时修改脚本并保存,并且以前的版本出错,则在回溯读出中,它会打开文件的当前版本并使回溯看起来与您开始时不同。我自己也见过好几次了。
【解决方案2】:

当你运行一个 python 程序并启动解释器时,首先发生的事情如下:

  • 模块sysbuiltins被初始化
  • __main__ 模块已初始化,这是您作为参数提供给解释器的文件; 这会导致您的代码执行

当一个模块被初始化时,它的代码被运行,在进程中定义类、变量和函数。您的模块(即主文件)的第一步可能是导入其他模块,这些模块将再次以相同的方式初始化;然后,它们生成的命名空间可供您的模块使用。导入过程的结果部分是内存中的模块(python-)对象。此对象确实有 fields that point to the .py and .pyc 内容,但不再评估这些内容:module objects are cached 并且它们的源永远不会运行两次。因此,之后在磁盘上修改模块对执行没有影响。当出于内省目的而读取源代码时(例如抛出异常时,或通过模块 inspect),它可能会产生影响。

这就是为什么在添加不打算在导入模块时运行的代码时检查if __name__ == "__main__" 是必要的。将文件作为 main 运行相当于正在导入该文件,但 __name__ 具有不同的值。


来源:

【讨论】:

    【解决方案3】:

    这是一个有趣的问题。答案是“视情况而定”。

    考虑以下代码:

    "Example script showing bad things you can do with python."
    
    import os
    
    print('this is a good script')
    with open(__file__, 'w') as fdesc:
        fdesc.write('print("this is a bad script")')
    
    import bad
    

    尝试将以上内容保存为“/tmp/bad.py”,然后执行“cd /tmp”,最后执行“python3 bad.py”,看看会发生什么。

    在我的 ubuntu 20 系统上,我看到了输出:

    this is a good script
    this is a bad script
    

    同样,您的问题的答案是“视情况而定”。如果你不做任何时髦的事情,那么脚本就在内存中,你就没事了。但是 python 是一种非常动态的语言,所以有多种方法可以修改你的“脚本”并让它影响输出。

    如果您不想做任何时髦的事情,那么可能需要注意的事情之一就是函数内部的导入。

    下面是另一个说明这个想法的例子(保存为“/tmp/modify.py”并执行“cd /tmp”然后“python3 modify.py”运行)。下面定义的 fiddle 函数模拟您在脚本运行时修改脚本(如果需要,您可以删除 fiddle 函数,在倒数第二行放入 time.sleep(300),然后自己修改文件)。

    关键是因为show 函数是在函数内部而不是在模块顶部进行导入,所以在调用函数之前不会进行导入。如果您在调用show 之前修改了脚本,那么将使用您修改后的脚本版本。

    如果您在修改正在运行的脚本时看到令人惊讶或意外的行为,我建议您在函数中查找 import 语句。有时做这种事情有充分的理由,所以你会不时在人们的代码和一些库中看到它。

    以下是函数内部导入如何导致奇怪效果的演示。您可以按原样尝试此操作,而不是注释掉对 fiddle 函数的调用,以查看在运行时修改脚本的效果。

    "Example showing import in a function"
    
    import time
    
    def yell(msg):
        "Yell a msg"
        return f'#{msg}#'
        
    def show(msg):
        "Print a message nicely"
        import modify
        print(modify.yell(msg))
    
    def fiddle():
        orig = open(__file__).read()
        with open(__file__, 'w') as fdesc:
            modified = orig.replace('{' + 'msg' + '}', '{msg.upper()}')
            fdesc.write(modified)
    
    fiddle()        
    show('What do you think?')
    

    【讨论】:

      【解决方案4】:

      不,结果将不会反映保存后的更改。运行常规 python 文件时结果不会改变。您必须保存更改并重新运行程序。

      【讨论】:

        【解决方案5】:

        如果您运行以下脚本:

        from time import sleep
        
        print("Printing hello world in: ")
        for i in range(10, 0, -1):
            print(f"{i}...")
            sleep(1)
            
        print("Hello World!")
        

        然后更改“Hello World!”到“你好 StackOverflow!”在倒计时时,它仍然会输出“Hello World”。

        【讨论】:

        • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
        • 请问?这一点点都不清楚或值得一票否决?它提供了一个“需要很长时间才能运行的 python 脚本”的示例,并解释了会发生什么。
        【解决方案6】:

        什么也没发生。一旦脚本加载到内存中并运行,它将保持这样的状态。

        “自动重新加载”功能可以在您的代码中实现,就像 Flask 和其他框架一样。

        【讨论】:

          【解决方案7】:

          这与您在问题中描述的略有不同,但它有效:

          my_string = "Hello World!"
          
          line = input(">>> ")
          exec(line)
          
          print(my_string)
          

          试运行:

          >>> print("Hey")
          Hey
          Hello World!
          
          >>> my_string = "Goodbye, World"
          Goodbye, World
          

          看,您可以动态更改“加载”代码的行为。

          【讨论】:

            【解决方案8】:

            取决于。如果 python 脚本链接到其他修改过的文件,那么当然会加载更新的版本。但是如果源不指向任何其他文件,只要它运行,它就会从缓存中运行所有脚本。下次更改将可见...

            如果关于在进行更改时自动应用更改 - 是的,@pcbacterio 是正确的。它可以做 thar 但脚本只记住最后一个动作/正在做的事情并检查文件何时被修改以重新运行它(所以它几乎不可见)

            =]

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-04-27
              • 1970-01-01
              • 2018-07-07
              • 2023-04-04
              • 1970-01-01
              相关资源
              最近更新 更多