Хотя перезагрузка функций не является функцией функции reload
, она все еще возможна. Я бы не рекомендовал делать это в производстве, но вот как это работает:
Функция, которую вы хотите заменить, является объектом где-то в памяти, и ваш код может содержать множество ссылок на него (а не на имя функции). Но то, что фактически делает эта функция при вызове, сохраняется не в этом объекте, а в другом объекте, на который, в свою очередь, ссылается объект функции в его атрибуте __code__
. Поэтому, если у вас есть ссылка на функцию, вы можете обновить ее код:
Модуль mymod:
from __future__ import print_function
def foo():
print("foo")
Другой сеанс модуля / Python:
>>> import mymod
>>> mymod.foo()
foo
>>> old_foo_ref = mymod.foo
>>> # edit mymod.py to make function "foo" print "bar"
>>> reload(mymod)
<module 'mymod' from 'mymod.py'>
>>> 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
>>>
Теперь, если у вас нет ссылки на старую функцию (то есть lambda
, переданную в другую функцию), вы можете попытаться получить ее с помощью модуля gc
, выполнив поиск в списке все объекты:
>>> 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__','') == '<lambda>'] # get all lambdas
[<function <lambda> at 0x7f315bf02f50>, <function <lambda> 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 #
>>>