CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: モジュールを動的にロードする

Python のモジュールは通常であれば import 文を使ってあらかじめ静的にロードする。 では、プログラムの中で動的にロードしたい場合にはどうしたらいいのか?というのが今回の話。 結論から先に言うと今は標準ライブラリの importlib を使うのが推奨される方法のようだ。

使った環境は次の通り。

$ python --version
Python 3.5.1
$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.11.3
BuildVersion:   15D21

下準備

まずは適当に Python のモジュールを作っておく。 Python のモジュールというのは単なる Python ファイルのことだ。

$ cat << 'EOF' > helloworld.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-


def greet():
    print('Hello, World!')

EOF

動的にロードしてみる

ファイル名が helloworld.py であれば Python からは helloworld モジュールとしてインポートできる。 まずは通常通り import 文を使って helloworld モジュールをロードしてみよう。 helloworld.py があるのと同じディレクトリで Python の REPL を起動して次を実行する。

$ python
>>> import helloworld
>>> helloworld.greet()
Hello, World!

ちゃんとモジュールがロードできていることが分かる。

次に importlib を使ってロードする方法を試してみよう。 importlib を使ってモジュールをロードするには import_module() 関数を使う。 この関数にはモジュール名を文字列で指定すると返り値としてモジュールが得られる。

>>> import importlib
>>> module = importlib.import_module('helloworld')
>>> module.greet()
Hello, World!
>>> module.__name__
'helloworld'

ばっちりだ。

名前を指定してロードしたい

先ほどの import_module() 関数ではモジュール名の指定ができなかった。 モジュール名を指定してロードしたい場合には importlib の中でもより低レベルな API を使う必要がある。 具体的には importlib.machinery の SourceFileLoader を使う。 このクラスはモジュール名と、その名前で読み込む Python ファイルを指定してインスタンス化することになっている。 得られたインスタンスの load_module() メソッドを呼び出すことでモジュールが手に入るという寸法だ。

>>> from importlib import machinery
>>> loader = machinery.SourceFileLoader('mymodule', 'helloworld.py')
>>> module = loader.load_module()
>>> module.greet()
Hello, World!
>>> module.__name__
'mymodule'

指定した通り helloworld.py が mymodule という名前のモジュールとしてロードされていることがわかる。

注意点

標準ライブラリの importlib は Python 3.3 から導入されたモジュールだ。 以前は同様の機能を提供する標準ライブラリとして imp があったものの、これは 3.x では非推奨となっている。 Python 2.7 にも一応は importlib が存在するものの、限定的な機能を提供するサブセットになっている。 もし Python 2.x でもフルセットの importlib が使いたいときはバックポート版を PyPI からインストールする必要がある。

$ pip install importlib

めでたしめでたし。