imp.load_source
在动态载入 python 模块时非常有用,但需要注意其特性。
假设我们有一个很简单的模块./a.py
:
# ./a.py
print("init ./a.py globally")
G = 0
然后测试一下import
和imp.load_source
的行为:
# ./b.py
import imp
import a
a.G = 2
print(a, id(a), a.G)
import a
a.G = 3
print(a, id(a), a.G)
import a
a.G = 4
print(a, id(a), a.G)
b = imp.load_source("a", "./a.py")
b.G = 5
print(b, id(b), b.G, a.G)
c = imp.load_source("a", "./a.py")
c.G = 6
print(c, id(c), c.G, b.G, a.G)
d = imp.load_source("b", "./a.py")
d.G = 7
print(d, id(d), d.G, c.G, b.G, a.G)
e = imp.load_source("b", "./a.py")
e.G = 8
print(e, id(e), e.G, d.G, c.G, b.G, a.G)
那么python ./b.py
的结果为:
init ./a.py globally
<module 'a' from '/home/zhangzq/room/a.py'> 140534289436320 2
<module 'a' from '/home/zhangzq/room/a.py'> 140534289436320 3
<module 'a' from '/home/zhangzq/room/a.py'> 140534289436320 4
init ./a.py globally
<module 'a' from './a.py'> 140534289436320 5 5
init ./a.py globally
<module 'a' from './a.py'> 140534289436320 6 6 6
init ./a.py globally
<module 'b' from './a.py'> 140534288844496 7 6 6 6
init ./a.py globally
<module 'b' from './a.py'> 140534288844496 8 8 6 6 6
总结如下:
- 重复
import
不会重复执行模块内容。 imp.load_source
总是会执行模块内容,即使重复导入和相同名字。- 不同的名字会生成不同的模块,同样名字生成同样模块。不同模块不会互相影响。指向相同模块时,在修改其中一个变量的内部成员时,另外一个变量也随之而变,因此需要特别小心。
因此需要特别注意以相同名字载入模块的情况,此时模块被重新执行,但又指向同样模块,修改时会影响对方,特别有迷惑性!
一个工程实践是,在载入时缓存,如果判断已经载入过,直接取出返回。这样可以避免模块被重复执行,尤其是当执行消耗比较大的情况下。
Q. E. D.