在这个实例中允许Python猴子修补的机制是什么?

时间:2015-01-28 05:45:01

标签: python global-variables monkeypatching python-internals side-effects

有人可以用Python解释器解释这背后的逻辑吗?这种行为只是本地线程吗?为什么第二个模块导入后第一个模块导入中的赋值仍然存在?我刚刚进行了长时间的调试会议。

external_library.py

def the_best():
    print "The best!"

modify_external_library.py

import external_library

def the_best_2():
    print "The best 2!"

external_library.the_best = the_best_2

main.py

import modify_external_library

import external_library

external_library.the_best()

测试:

$ python main.py
The best 2!

4 个答案:

答案 0 :(得分:9)

没有关于此的线程本地。 somemodule.anattr = avalue 非常全球行为!在此分配之后,无论如何,属性都会更改(直到可能稍后更改)。

没有神秘的机制在起作用!对允许这种赋值的对象的任何属性的赋值(如模块对象那样)只是以明显的方式工作 - 没有线程本地的东西,没有什么奇怪的 - 并且属性的赋值仍然存在,只要对象的属性为你&#当然,我已经分配了坚持。

重复的import external_library没有重新加载模块(reload是一个完全独立的内置,import调用它!) - 它只检查sys.modules,在external_library中找到dict键,并将相应的值(之前由该赋值修改)绑定到名称{{ 1}}在相应的命名空间中(这里是模块external_library的全局变量)。

答案 1 :(得分:1)

模块是新式类的实例。修改模块的属性(在本例中为函数)时,您正在修改模块实例。当您尝试再次导入它时(使用import external_library),您只需获取modify_external_library.py内已引用的相同模块对象。

编辑:当然,尝试再次导入同一个模块并不能真正起作用(正如Alex Martelli指出的那样)。加载后,除非使用reload明确执行,否则不会重新初始化模块。

答案 2 :(得分:1)

正如亚历克斯所说,你需要重新加载external_library,如果它已经被导入,只是导入它就什么都不做。您可以将print语句放入external_librarymodify_external_library模块中进行检查。

import modify_external_library

#import external_library
reload(external_library)

external_library.the_best()

<强>输出

The best!

答案 3 :(得分:0)

Monkey补丁是可行的,因为类在python中是可修改的,但允许它像这样传播的机制是,一旦导入任何模块并初始化,稍后导入只需将现有实例添加到本地命名空间而不重新运行初始化,这也节省了模块进行大量初始化以及允许猴子补丁的时间。