Following up on this question regarding reloading a module, how do I reload a specific function from a changed module?
pseudo-code:
from foo import b
While reloading of functions is not a feature of the reload
function, it is still possible. I would not recommend doing it in production, but here is how it works:
The function you want to replace is a object somewhere in memory, and your code might hold many references to it (and not to the name of the function). But what that function actually does when called is not saved in that object, but in another object that, in turn, is referenced by the function object, in its attribute __code__
. So as long as you have a reference to the function, you can update its code:
Module mymod:
from __future__ import print_function
def foo():
print("foo")
Other module / python session:
>>> import mymod
>>> mymod.foo()
foo
>>> old_foo_ref = mymod.foo
>>> # edit mymod.py to make function "foo" print "bar"
>>> reload(mymod)
>>> old_foo_ref() # old reference is running old code
foo
>>> mymod.foo() # reloaded module has new code
bar
>>> old_foo_ref.__code__ = mymod.foo.__code__
>>> old_foo_ref() # now the old function object is also running the new code
bar
>>>
Now, in case you do not have a reference to the old function (i.e. a lambda
passed into another function), you can try to get a hold of it with the gc
module, by searching the list of all objects:
>>> def _apply(f, x):
... return lambda: f(x)
...
>>> tmp = _apply(lambda x: x+1, 1) # now there is a lambda we don't have a reference to
>>> tmp()
2
>>> import gc
>>> lambdas = [obj for obj in gc.get_objects() if getattr(obj,'__name__','') == ''] # get all lambdas
[ at 0x7f315bf02f50>, at 0x7f315bf12050>]
# i guess the first one is the one i passed in, and the second one is the one returned by _apply
# lets make sure:
>>> lambdas[0].__code__.co_argcount
1 # so this is the "lambda x: ..."
>>> lambdas[1].__code__.co_argcount
0 # and this is the "lambda: ..."
>>> lambdas[0].__code__ = (lambda x: x+2).__code__ # change lambda to return arg + 2
>>> tmp()
3 #
>>>