Comment recharger la fonction d'un module en Python?

suite à cette question concernant le rechargement d'un module , comment recharger une fonction spécifique à partir d'un module modifié?

pseudo-code:

from foo import bar

if foo.py has changed:
    reload bar
20
demandé sur Community 2011-09-01 17:34:03

7 réponses

ce que vous voulez est possible, mais nécessite de recharger deux choses... d'abord reload(foo) , mais ensuite vous devez aussi reload(baz) (en supposant que baz est le nom du module contenant la déclaration from foo import bar ).

quant à la raison... Lorsque foo est chargé pour la première fois, un objet foo est créé, contenant un objet bar . Lorsque vous importez bar dans le module baz , il stocke une référence à bar . Quand reload(foo) est appelé, l'objet foo est effacé, et le module ré-exécuté. Cela signifie que toutes les références foo sont toujours valables, mais un nouvel objet bar a été créé... donc toutes les références qui ont été importées quelque part sont toujours des références à l'objet Vieux bar . En rechargeant baz , vous lui faites réimporter le nouveau bar .


alternativement, vous pouvez juste faire import foo en votre module, et toujours appeler foo.bar() . De cette façon , à chaque fois que vous reload(foo) , vous obtiendrez la plus récente référence bar .

4
répondu Eli Collins 2011-09-01 19:09:24

recharger à chaud n'est pas quelque chose que vous pouvez faire en python de manière fiable sans faire exploser votre tête. Vous ne pouvez littéralement pas supporter le rechargement sans avoir écrit le code de manière spéciale, et essayer d'écrire et de maintenir le code qui supporte le rechargement avec n'importe quelle raison exige une discipline extrême et est trop confus pour être la peine de l'effort. Tester un tel code n'est pas une tâche facile non plus.

la solution est de redémarrer complètement le processus Python lorsque le code a changé. Il est possible de faites cela de manière transparente, mais comment dépend de votre domaine de problème spécifique.

10
répondu Mike Graham 2011-09-01 13:51:28

à partir d'aujourd'hui, la bonne façon de faire ceci est:

import sys, importlib
importlib.reload(sys.modules['foo'])
from foo import bar

testé sur python 2.7, 3.5, 3.6.

7
répondu Antony Hatchkins 2017-10-18 15:37:25

alors que le rechargement des fonctions n'est pas une caractéristique de la fonction reload , il est encore possible. Je ne recommande pas de le faire en production, mais voici comment cela fonctionne: La fonction que vous voulez remplacer est un objet quelque part dans la mémoire, et votre code pourrait contenir de nombreuses références (et non le nom de la fonction). Mais ce que cette fonction fait réellement lorsqu'elle est appelée n'est pas sauvegardée dans cet objet, mais dans un autre objet qui, à son tour, est référencé par l'objet de la fonction, dans son l'attribut __code__ . Donc, tant que vous avez une référence à la fonction, vous pouvez mettre à jour son code:

Module mymod:

from __future__ import print_function
def foo():
    print("foo")

autre module / session 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
>>> 

maintenant, dans le cas où vous n'avez pas de référence à l'ancienne fonction (i.e. un lambda passé dans une autre fonction), vous pouvez essayer de mettre la main dessus avec le module gc , en cherchant la liste de tous les objets: "15198090920"

>>> 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  # 
>>> 
3
répondu pixelbrei 2015-12-11 09:01:23

Vous ne pouvez pas recharger une méthode à partir d'un module, mais vous pouvez charger le module avec un nouveau nom, dire foo2 et dire bar = foo2.bar pour remplacer la référence actuelle.

notez que si bar a des dépendances sur d'autres choses dans foo ou tout autre effet secondaire, vous allez avoir des problèmes. Donc, bien que cela fonctionne, cela ne fonctionne que pour les cas les plus simples.

2
répondu Aaron Digulla 2011-09-01 13:58:22

vous devrez utiliser reload pour recharger le module, car vous ne pouvez pas recharger seulement la fonction:

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>>
1
répondu Roshan Mathews 2011-09-01 13:42:32

Si vous êtes l'auteur de foo.py , vous pouvez écrire:

with open('bar.py') as f: bar_code=compile(f.read(),'bar.py','exec')

def bar(a,b):
    exec(bar_code)

def reload_bar():
    global bar_code
    with open('bar.py') as f: bar_code=compile(f.read(),'bar.py','exec') 

puis, dans votre pseudo, rechargez si bar.py a changé. Cette approche est particulièrement agréable lorsque bar vit dans le même module que le code qui veut le recharger à chaud plutôt que dans le cas de L'OP où il vit dans un module différent.

1
répondu Eponymous 2015-10-11 18:02:55