Python 自修改文件:调用与导入

Python 自修改文件:调用与导入,python,file-access,Python,File Access,免责声明:这是关于疯狂边缘案例中奇怪的语言行为。我用它来问一个关于python和内存/磁盘读写的更大问题。动态编辑文件并随后调用与导入文件然后调用有何不同?在从内存加载/从磁盘加载方面,导入同一文件与导出外部模块是否不同?触发从磁盘重新加载函数的条件 我试图理解python函数加载到内存中的方式以及从磁盘重新读取它们的时间。我编写了一个简单的脚本temp.py,它通过调用modify_来修改自身。这个函数在打印行中写入。没有什么疯狂的、可预测的行为,调用时会添加打印语句 import dis

免责声明:这是关于疯狂边缘案例中奇怪的语言行为。我用它来问一个关于python和内存/磁盘读写的更大问题。动态编辑文件并随后调用与导入文件然后调用有何不同?在从内存加载/从磁盘加载方面,导入同一文件与导出外部模块是否不同?触发从磁盘重新加载函数的条件

我试图理解python函数加载到内存中的方式以及从磁盘重新读取它们的时间。我编写了一个简单的脚本temp.py,它通过调用modify_来修改自身。这个函数在打印行中写入。没有什么疯狂的、可预测的行为,调用时会添加打印语句

import dis

def modify_this_function():
    f = open("temp.py", "r")
    contents = f.readlines()
    f.close()

    contents.insert(3, '\tprint("hello!")\n')

    f = open("temp.py", "w")
    contents = "".join(contents)
    f.write(contents)
    f.close()

modify_this_function()
print(dis.dis(modify_this_function))
modify_this_function()
这个调用没有做任何特别有趣的事情,尽管它在重新加载时修改了文件,但添加了两个print语句。调用dis.dis的输出反映了原始函数定义

  4           0 LOAD_GLOBAL              0 (open)
              3 LOAD_CONST               1 ('temp.py')
              6 LOAD_CONST               2 ('r')
              9 CALL_FUNCTION            2
             12 STORE_FAST               0 (f)

  5          15 LOAD_FAST                0 (f)
             18 LOAD_ATTR                1 (readlines)
             21 CALL_FUNCTION            0
             24 STORE_FAST               1 (contents)

  6          27 LOAD_FAST                0 (f)
             30 LOAD_ATTR                2 (close)
             33 CALL_FUNCTION            0
             36 POP_TOP             

  8          37 LOAD_FAST                1 (contents)
             40 LOAD_ATTR                3 (insert)
             43 LOAD_CONST               3 (3)
             46 LOAD_CONST               4 ('\tprint("hello!")\n')
             49 CALL_FUNCTION            2
             52 POP_TOP             

 10          53 LOAD_GLOBAL              0 (open)
             56 LOAD_CONST               1 ('temp.py')
             59 LOAD_CONST               5 ('w')
             62 CALL_FUNCTION            2
             65 STORE_FAST               0 (f)

 11          68 LOAD_CONST               6 ('')
             71 LOAD_ATTR                4 (join)
             74 LOAD_FAST                1 (contents)
             77 CALL_FUNCTION            1
             80 STORE_FAST               1 (contents)

 12          83 LOAD_FAST                0 (f)
             86 LOAD_ATTR                5 (write)
             89 LOAD_FAST                1 (contents)
             92 CALL_FUNCTION            1
             95 POP_TOP             

 13          96 LOAD_FAST                0 (f)
             99 LOAD_ATTR                2 (close)
            102 CALL_FUNCTION            0
            105 POP_TOP             
            106 LOAD_CONST               0 (None)
            109 RETURN_VALUE        
None
这似乎表明函数在内存中,而不是从磁盘重新加载。对吗

import dis

def modify_this_function():
    f = open("temp.py", "r")
    contents = f.readlines()
    f.close()

    contents.insert(3, '\tprint("hello!")\n')

    f = open("temp.py", "w")
    contents = "".join(contents)
    f.write(contents)
    f.close()

modify_this_function()
print(dis.dis(modify_this_function))
#modify_this_function()
from temp import modify_this_function
这个函数调用更有趣

  4           0 LOAD_GLOBAL              0 (open)
              3 LOAD_CONST               1 ('temp.py')
              6 LOAD_CONST               2 ('r')
              9 CALL_FUNCTION            2
             12 STORE_FAST               0 (f)

  5          15 LOAD_FAST                0 (f)
             18 LOAD_ATTR                1 (readlines)
             21 CALL_FUNCTION            0
             24 STORE_FAST               1 (contents)

  6          27 LOAD_FAST                0 (f)
             30 LOAD_ATTR                2 (close)
             33 CALL_FUNCTION            0
             36 POP_TOP             

  8          37 LOAD_FAST                1 (contents)
             40 LOAD_ATTR                3 (insert)
             43 LOAD_CONST               3 (3)
             46 LOAD_CONST               4 ('\tprint("hello!")\n')
             49 CALL_FUNCTION            2
             52 POP_TOP             

 10          53 LOAD_GLOBAL              0 (open)
             56 LOAD_CONST               1 ('temp.py')
             59 LOAD_CONST               5 ('w')
             62 CALL_FUNCTION            2
             65 STORE_FAST               0 (f)

 11          68 LOAD_CONST               6 ('')
             71 LOAD_ATTR                4 (join)
             74 LOAD_FAST                1 (contents)
             77 CALL_FUNCTION            1
             80 STORE_FAST               1 (contents)

 12          83 LOAD_FAST                0 (f)
             86 LOAD_ATTR                5 (write)
             89 LOAD_FAST                1 (contents)
             92 CALL_FUNCTION            1
             95 POP_TOP             

 13          96 LOAD_FAST                0 (f)
             99 LOAD_ATTR                2 (close)
            102 CALL_FUNCTION            0
            105 POP_TOP             
            106 LOAD_CONST               0 (None)
            109 RETURN_VALUE        
None
hello!
  4           0 LOAD_CONST               1 ('hello!')
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  5           5 LOAD_GLOBAL              0 (open)
              8 LOAD_CONST               2 ('temp.py')
             11 LOAD_CONST               3 ('r')
             14 CALL_FUNCTION            2
             17 STORE_FAST               0 (f)

  6          20 LOAD_FAST                0 (f)
             23 LOAD_ATTR                1 (readlines)
             26 CALL_FUNCTION            0
             29 STORE_FAST               1 (contents)

  7          32 LOAD_FAST                0 (f)
             35 LOAD_ATTR                2 (close)
             38 CALL_FUNCTION            0
             41 POP_TOP             

  9          42 LOAD_FAST                1 (contents)
             45 LOAD_ATTR                3 (insert)
             48 LOAD_CONST               4 (3)
             51 LOAD_CONST               5 ('\tprint("hello!")\n')
             54 CALL_FUNCTION            2
             57 POP_TOP             

 11          58 LOAD_GLOBAL              0 (open)
             61 LOAD_CONST               2 ('temp.py')
             64 LOAD_CONST               6 ('w')
             67 CALL_FUNCTION            2
             70 STORE_FAST               0 (f)

 12          73 LOAD_CONST               7 ('')
             76 LOAD_ATTR                4 (join)
             79 LOAD_FAST                1 (contents)
             82 CALL_FUNCTION            1
             85 STORE_FAST               1 (contents)

 13          88 LOAD_FAST                0 (f)
             91 LOAD_ATTR                5 (write)
             94 LOAD_FAST                1 (contents)
             97 CALL_FUNCTION            1
            100 POP_TOP             

 14         101 LOAD_FAST                0 (f)
            104 LOAD_ATTR                2 (close)
            107 CALL_FUNCTION            0
            110 POP_TOP             
            111 LOAD_CONST               0 (None)
            114 RETURN_VALUE        
None

这里,似乎调用了print,它出现在反汇编程序输出中。那么这是否表示导入触发了从磁盘的重新读取?不是通过简单调用函数启动的重新读取。我的直觉正确吗?

您正在加载模块两次。这通常是不可能的,因为Python缓存模块对象,但这是可能的。这一次之所以发生,是因为您第一次将其作为名为_umain_;的主模块加载。第二次将其加载为其正常名称temp。第二次,您将看到模块在再次加载导入之前对其自身文件所做修改的结果

导入模块后,对加载该模块的文件所做的更改不会反映在模块的代码中,这些代码都是在加载模块时编译的。在某些情况下,这样的更改可能会混淆一些调试工具,导致它们读取错误版本的源代码,并误报错误位置和其他详细信息,但它们不会对代码的运行方式产生任何影响


在Python的现代版本中,您还可以使用importlib中的reload函数,这是Python 2中的内置函数,用于按需重新加载模块。新版本模块的内容将覆盖原始版本。但是请注意,对来自旧版本模块的对象的任何外部引用将仍然指向相同的旧对象。

A是否重新读取?您最初是什么时候导入temp.py的?无论如何,是的,您的函数不会做任何可能导致python重新读取和加载模块的事情。我甚至从来没有考虑过模块导入自身的情况,所以我会等待那些比我有更多神秘知识的人。所以,一般来说,如果你导入模块,然后在其他地方再次导入模块,Python实际上不会重新加载模块。然而,当它自己导入时,它确实做到了这一点,我用一个简单的例子temp.py尝试了它:print'hello';导入温度。这种情况很可能是因为当使用python module.py加载模块时,模块的名称空间是_main__;,然后当它将自身导入为temp时,会重新加载。没有无限递归,因为它不会在再次点击导入临时文件时继续重新加载。但动态修改源代码文件永远不会导致重新加载这些文件。要明确地重新加载文件,需要使用import importlib;importlib.reload仅发生一次。再次尝试修改函数,然后再次使用import temp,我认为您不会看到任何进一步的修改,因为导入系统会将temp识别为已加载。