在这种情况下允许 Python 猴子修补的机制是什么?

What is the mechanism that allows Python monkey patching in this instance?

有人可以解释一下 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!

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

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

明确地这样做

与此无关。 somemodule.anattr = avalue 非常 的全球行为!在这个赋值之后,无论如何都会改变属性(直到以后可能改回)。

游戏中没有神秘的机制!对允许此类赋值的对象的任何属性的赋值(如模块对象所做的那样)只是以明显的方式工作——没有线程局部的任何东西,没有什么奇怪的——并且对属性的赋值持续存在,只要你的属性的对象是当然,我们分配了持久性。

重复的 import external_library 不会 重新加载 模块(reload 是一个完全独立的内置函数,而 importnot call it!) -- 它只是检查 sys.modules,在那个 dict 中找到一个 external_library 键,并绑定相应的值(之前被那个修改过的值)赋值)到适当命名空间中的名称 external_library(这里是模块 main 的全局变量)。

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

正如 Alex 指出您需要重新加载 external_library,如果它已经被导入,那么简单地导入将不会执行任何操作。您可以通过将 print 语句放入 external_librarymodify_external_library 模块来检查。

import modify_external_library

#import external_library
reload(external_library)

external_library.the_best()

输出

The best!