免责声明:这是关于疯狂边缘案件中奇怪的语言行为。我用它来询问有关python和内存/磁盘读写的更大问题。如何动态编辑文件并随后调用与导入文件然后调用不同?导入与从内存加载外部模块相同的文件/从磁盘加载?什么条件触发从磁盘重新加载函数。
我试图理解python函数加载到内存中的方式(以及从磁盘重新读取它们的方式)。我写了一个简单的脚本(temp.py),它在调用modify_this_function
时修改了它自己,它在一个打印行中写入。没有任何疯狂的,可预测的行为,在调用时会添加一个print语句。
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()
此调用没有做任何特别有趣的事情(尽管它在重新加载时修改了文件,添加了两个打印语句)。对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并且它出现在反汇编输出中。那么这是否表明导入触发了从磁盘重读?重读不是通过简单地调用函数来启动的。我的直觉在这里是否正确?
答案 0 :(得分:1)
您正在加载模块两次。这通常不可能,因为Python会缓存模块对象,但它可能会发生。这次是因为您第一次将其作为主模块加载,名为__main__
。第二次将其加载为其正常名称temp
。第二次,您将看到模块在再次加载的import
之前对其自己的文件所做的修改的结果。
导入模块后,对其加载的文件的更改将不会反映在模块的代码中,该代码在加载模块时全部编译。这些更改可能将某些调试工具混淆为读取错误版本的源并在某些情况下误报错误位置和其他详细信息,但它们不会对代码的运行方式产生任何影响。
您还可以使用reload
函数(来自现代版本的Python中的importlib
,内置于Python 2中)来按需重新加载模块。新版本模块的内容将覆盖原始版本。请注意,对旧模块模块中对象的任何外部引用仍将指向相同的旧对象。