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