如果我在运行时修改Python脚本会发生什么?

371 投票
9 回答
93166 浏览
提问于 2025-04-16 13:39

想象一下,有一个Python脚本需要很长时间才能完成。如果我在它运行的时候去修改这个脚本,会发生什么呢?结果会不会不一样?

9 个回答

18

这是个有趣的问题。答案是“这要看情况”。

考虑以下代码:

"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”,然后进入“/tmp”目录,最后运行“python3 bad.py”,看看会发生什么。

在我的Ubuntu 20系统上,我看到的输出是:

this is a good script
this is a bad script

所以,再次强调,你的问题的答案是“这要看情况”。如果你没有做什么奇怪的事情,那么脚本就在内存中,你就没问题。但Python是一种非常灵活的语言,所以有很多方法可以修改你的“脚本”,并影响输出。

如果你不想做什么奇怪的事情,那么需要注意的一点是函数内部的导入。

下面是另一个例子,说明这个概念(保存为“/tmp/modify.py”,然后进入“/tmp”目录,再运行“python3 modify.py”)。下面定义的fiddle函数模拟了你在脚本运行时修改它的情况(如果你愿意,可以去掉fiddle函数,在倒数第二行加上time.sleep(300),然后自己修改文件)。

关键是,因为show函数在函数内部进行导入,而不是在模块的顶部,所以导入不会发生,直到调用这个函数。如果你在调用show之前修改了脚本,那么你修改后的版本就会被使用。

如果你在修改正在运行的脚本时看到意外或奇怪的行为,我建议你检查一下函数内部的导入语句。有时候这样做是有道理的,所以你会在一些人的代码和某些库中看到这种情况。

下面是一个演示,说明函数内部的导入如何导致奇怪的效果。你可以尝试直接运行这个代码,或者注释掉对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?')
28

当你运行一个Python程序并启动解释器时,首先会发生以下几件事:

  • 模块 sysbuiltins 会被初始化
  • 初始化 __main__ 模块,也就是你传给解释器的文件;这会导致你的代码开始执行

当一个模块被初始化时,它的代码会被运行,这个过程会定义类、变量和函数。你的模块(也就是主文件)的第一步通常是导入其他模块,这些模块也会以同样的方式被初始化;它们的命名空间会被提供给你的模块使用。导入的结果部分是一个在内存中的模块(Python)对象。这个对象确实有一些字段指向 .py 和 .pyc 的内容,但这些内容不会再被评估:模块对象是被缓存的,它们的源代码不会被运行两次。因此,在磁盘上修改模块后对执行没有影响。不过,当源代码被读取用于检查目的时,比如抛出异常时,或者通过模块 inspect 时,可能会有影响。

这就是为什么在添加不希望在模块导入时运行的代码时,需要检查 if __name__ == "__main__"。将文件作为主程序运行相当于该文件被导入,唯一的区别是 __name__ 的值不同。


来源:

435

没什么特别的,因为Python会把你的脚本预先编译成一个PYC文件,然后运行这个文件。

不过,如果发生了某种错误,你可能会得到一个有点误导的解释,因为X行的代码可能和你开始运行脚本之前的代码不一样。

撰写回答