How does one monkey patch a function in python?

guidoism picture guidoism · Mar 3, 2010 · Viewed 44.4k times · Source

I'm having trouble replacing a function from a different module with another function and it's driving me crazy.

Let's say I have a module bar.py that looks like this:

from a_package.baz import do_something_expensive

def a_function():
    print do_something_expensive()

And I have another module that looks like this:

from bar import a_function
a_function()

from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'
a_function()

import a_package.baz
a_package.baz.do_something_expensive = lambda: 'Something really cheap.'
a_function()

I would expect to get the results:

Something expensive!
Something really cheap.
Something really cheap.

But instead I get this:

Something expensive!
Something expensive!
Something expensive!

What am I doing wrong?

Answer

Nicholas Riley picture Nicholas Riley · Mar 3, 2010

It may help to think of how Python namespaces work: they're essentially dictionaries. So when you do this:

from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'

think of it like this:

do_something_expensive = a_package.baz['do_something_expensive']
do_something_expensive = lambda: 'Something really cheap.'

Hopefully you can realize why this doesn't work then :-) Once you import a name into a namespace, the value of the name in the namespace you imported from is irrelevant. You're only modifying the value of do_something_expensive in the local module's namespace, or in a_package.baz's namespace, above. But because bar imports do_something_expensive directly, rather than referencing it from the module namespace, you need to write to its namespace:

import bar
bar.do_something_expensive = lambda: 'Something really cheap.'