物理の駅 Physics station by 現役研究者

テクノロジーは共有されてこそ栄える

Python 動的にモジュールを作成、import、再import (リロード) する

Pythonはインタプリタなので、文字列から動的なimportが可能である。

Pythonのコードの中で、NewModule.py を作って、それを読み込んでみよう。

NewModule.py に書くテキスト

print(0)
a = 10 + 0

Pythonコード。import importlib が重要。

module_name = "NewModule"

import importlib

i=0
with open(f"{module_name}.py","w") as f:
    f.write(f"print({i})\n")
    f.write(f"a = 10 + {i}\n")

mod = importlib.import_module(module_name)
print(f"{module_name}.a = {mod.a}")

出力

0
NewModule.a = 10

では、NewModule.py をPython内で更新した場合はどうなるか?

module_name = "NewModule"

import time
import importlib

for i in range(10):
    with open(f"{module_name}.py","w") as f:
        f.write(f"print({i})\n")
        f.write(f"a = 10 + {i}\n")
        i+=1
    #キャッシュが効いて更新したファイルを読み込めないことがあるので1秒待つ
    time.sleep(1)
    
    mod = importlib.import_module(module_name)
    print(f"{module_name}.a = {mod.a}")

出力

0
NewModule.a = 10
NewModule.a = 10
NewModule.a = 10
NewModule.a = 10
NewModule.a = 10
NewModule.a = 10
NewModule.a = 10
NewModule.a = 10
NewModule.a = 10
NewModule.a = 10

Pythonユーザーならご存知だろうが、再importしても、モジュールは読み込まれない。なので、モジュールのPythonファイルを書き換えても、カーネル内のモジュールは更新はされない。

そこで、importlib.reloadを使って、再import(リロード)させる。

module_name = "NewModule"

import time
import sys
import importlib

for i in range(10):
    with open(f"{module_name}.py","w") as f:
        f.write(f"print({i})\n")
        f.write(f"a = 10 + {i}\n")
        i+=1
    #キャッシュが効いて更新したファイルを読み込めないことがあるので1秒待つ
    time.sleep(1)
    
    if module_name in sys.modules:
        print("reload")
        importlib.reload(importlib.import_module(module_name))
    mod = importlib.import_module(module_name)
    print(f"{module_name}.a = {mod.a}")

出力

0
NewModule.a = 10
reload
1
NewModule.a = 11
reload
2
NewModule.a = 12
reload
3
NewModule.a = 13
reload
4
NewModule.a = 14
以下略

以前のカーネルが再利用した場合は、最初にreloadが出る。

これで、Python内で動的にモジュールを作成、import、再import(リロード)ができた。

最後に、モジュールを再読み込みするだけのシンプルコード

import sys
import importlib

module_name = "global_a"

if module_name in sys.modules:
    print("reload",module_name)
    importlib.reload(importlib.import_module(module_name))
mod = importlib.import_module(module_name)

from global_a import *