なんとなくWebで見つけた今までの方法(imp.load_module)はdeprecatedだったため、Python 3.4におけるモジュールの動的読み込み処理を調べてみた。
ファイル構成
top
|- main.py
|- foo
|- __init__.py
|- some.py
|- sample.py
|- bar
|- __init__.py
|- some.py
古い方法
imp.load_module
を使う方法で、find_module()
の結果を使用してload_module()
でモジュールを読み込む。
このとき、モジュール名(属性 __name__
)は load_module
の第一引数name
が設定される。
Pythonではモジュールを sys.modules
に登録するが、後勝ちになって先に読み込んだモジュールは上書きされてしまうことに注意する。
このため、fooとbarの両方のsome.pyを同じ名前someで読み込むと、意図しないものになることがある。
import sys
import imp
def load(dir_name:str, symbol:str): # -> module
(file, path, description) = imp.find_module(dir_name + '/' + symbol)
return imp.load_module(symbol, file, path, description)
m = load('foo', 'some')
m.__name__ # -> 'some'
sys.modules['some'] # -> 'module'
n = load('bar', 'some')
m == n # -> True
新しい方法
imp
の置き換えとして importlib
を使う。
importlib.import_module
で、相対パスそのままでモジュールを読み込む。
import importlib
m = importlib.import_module('foo.some') # -> 'module'
m.__name__ # -> 'foo.some'
n = importlib.import_module('bar.some') # -> 'module'
n.__name__ # -> 'bar.some'
モジュール名を変えたいとき
古い方法と同様に、相対パス名を無視してモジュール名を自在に設定できるだろうか?
公式ドキュメントで imp.load_module
の置き換えとして紹介されているものに次がある。
SourceFileLoader()
を使い、その第一引数にモジュール名を設定することができる。
モジュール名(ここではsome)は、やはり後勝ちとなる。別の名前を設定することができるので、必要に応じて設定する。
import importlib.machinery as imm
datum = imm.SourceFileLoader('some', 'foo/some.py').load_module()
モジュール名の影響
foo/sample.pyでsome.pyを使用するとき、かつ動的に読み込む際の影響。
sampleを動的に読み込む場合、importで指定するモジュール名は sys.modules
への登録名となる。上でモジュール名を変えていたときは、ここのimportにも影響する。
import some # or
import foo.some
まとめ
- Python 3.4 におけるモジュールの動的読み込みを調べた
- importlibないしその配下のモジュールを使用する
- モジュール名の扱いに注意する