OregonTrail
OregonTrail

Reputation: 9039

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

Can someone explain the logic behind how this works with the Python interpreter? Is this behavior only thread local? Why does the assignment in the first module import persist after the second module import? I just had a long debugging session that came down to this.

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()

Test:

$ python main.py
The best 2!

Upvotes: 3

Views: 2416

Answers (4)

Eithos
Eithos

Reputation: 2491

Modules are instances of new-style classes. When you modify the attributes of a module (the function in this case), you are modifying the module instance. When you try to import it again (with import external_library), you're just getting the same module object already referenced inside of modify_external_library.py.

Edit: Of course, trying to import the same module again does not really work (as Alex Martelli points out). Once loaded, modules are not re-initialized unless done so explicitly with reload.

Upvotes: 2

PM 2Ring
PM 2Ring

Reputation: 55469

As Alex indicated you need to reload external_library, simply importing it will do nothing if it's already been imported. You can check that by putting print statements into your external_library and modify_external_library modules.

import modify_external_library

#import external_library
reload(external_library)

external_library.the_best()

output

The best!

Upvotes: 1

Steve Barnes
Steve Barnes

Reputation: 28370

Monkey patching works because classes are modifiable in python but the mechanism that allows it to spread like this is that once any module has been imported, and initialised, later imports simply add the existing instance to the local namespace without rerunning the initialisation, this also saves time when a module has a lot of initialisation as well as allowing monkey patches.

Upvotes: 0

Alex Martelli
Alex Martelli

Reputation: 881635

Nothing thread-local about this. somemodule.anattr = avalue is very global behavior! After this assignment the attribute is changed for good (until maybe changed back later) no matter what.

There's no mysterious mechanics at play! Assignment to any attribute of an object that allows such assignment (as module objects do) just work in the obvious way -- no thread-local anything, nothing strange -- and assignment to attribute persists, as long as the object whose attribute you've assigned persists, of course.

The repeated import external_library doesn't reload the module (reload is a totally separate builtin and import does not call it!) -- it just checks sys.modules, finds an external_library key in that dict, and binds the corresponding value (which was previously modified by that assignment) to name external_library in the appropriate namespace (here, globals of module main).

Upvotes: 10

Related Questions