2 votes

Comprendre la fonction `importlib.reload` de Python

J'essaie de comprendre comment la méthode importlib.reload se comporte réellement. Je vais donner un exemple concis

import importlib
import sys
from pathlib import Path
import gc

def write_dummy_class(return_value):
    target = Path(__file__).parent / 'test_reload_import.py'
    target.write_text(
        "class Dummy:\n"
        f"    var = {return_value}\n"
        "    def run(self):\n"
        "        print(f'Dummy.run(self) >> self.var = {id(self.var):x}')\n"
        f"        return self.var\n"
    )

write_dummy_class(1)

from test_reload_import import Dummy

print(f'id Dummy: {id(Dummy):x}')
print(Dummy.run)
assert Dummy().run() == 1, "Initial one failed??"

write_dummy_class(2)

old_module = sys.modules["test_reload_import"]
old_dummy = old_module.Dummy  # Keep a reference alive
print(f'Reloading, old module: {id(old_module):x}')
new_module = importlib.reload(old_module)
print(f'Reloaded, new module: {id(new_module):x}')

print(f'id new Dummy: {id(new_module.Dummy):x}')
print(f'id old Dummy: {id(old_dummy):x}')

print(f'id Dummy: {id(new_module.Dummy):x}')
print(new_module.Dummy.run)
new_run = new_module.Dummy().run()
assert new_run == 2, f'Dummy.run() returned {new_run} instead of 2.'

Voici le résultat :

id Dummy: 1dd320c0fa0
<function Dummy.run at 0x000001DD325CC700>
Dummy.run(self) >> self.var = 1dd31d06930
Reloading, old module: 1dd325c7950
Reloaded, new module: 1dd325c7950
id new Dummy: 1dd320c30d0
id old Dummy: 1dd320c0fa0
<function Dummy.run at 0x000001DD325CC790>
Dummy.run(self) >> self.var = 1dd31d06930
Traceback (most recent call last):
  File "test_reload.py", line 240, in <module>
    assert new_run == 2, f'Dummy.run() returned {new_run} instead of 2.'
AssertionError: Dummy.run() returned 1 instead of 2.

Observations :

  1. Le rechargement d'un module renvoie la même adresse mémoire que la précédente pour ce module.
  2. sont rechargés à l'intérieur du module ( Dummy a un autre identifiant).
  3. Mais ce qui m'étonne, c'est que l'adresse mémoire de la variable de classe 'Dummy.var' pointe toujours vers l'ancienne.

Quelqu'un pourrait-il m'expliquer cette dernière partie ? Comment se fait-il que la classe soit rechargée, mais que les variables de la classe ne le soient pas ? Le code n'est-il pas réinterprété ? Et en tant que tel, le var devrait être réinterprétée aussi, non ? Donc, en gros, on obtient une autre adresse mémoire ?

Ce qui m'amène à ma prochaine question : qu'est-ce qui n'est pas rechargé non plus ?

BTW, je sais que les petits entiers sont mappés aux mêmes adresses mémoire dans Python. Ce n'est pas ce qui est en jeu ici. Comme je change une variable de classe de 1 à 2, cela devrait être une autre adresse mémoire. Ou si c'est la même adresse, elle devrait avoir une valeur différente.

Mais après avoir rechargé la classe, l'adresse mémoire de la variable de classe n'est pas mise à jour, ce qui me laisse perplexe. Et ce qui m'amène à me demander quels autres objets présentent le même comportement.

(Version de Python : 3.9.9)

Oh, et une chose très étrange est que ce script fonctionne parfaitement bien lorsqu'il est exécuté sous "Debug" dans PyCharm. Mais quand il est exécuté sous "Run"... Il se casse à la 2ème assertion.

Merci beaucoup !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X