Pythonで動的にモジュールを呼び出す

概要

サーバーやユーザーによって呼び出すモジュールを変えて処理したい場合があるかと思います。そんなときは標準モジュールのimportlibを使うことで実現できます。コツとしてはスクリプト名とメソッド名を同じにすることです(クラス名は定義しません)。

背景

仕事上、ファイルの入出力をやることが多く、最近はテキストなどでリストを作っておいて globなどで読み込んでからfor文で処理することが多いです。

ところで、面倒くさがりな私はすべてのスクリプトに同じメソッドをコピペするのが嫌な人間ですので、よく使う処理は別のスクリプトに予めメソッドとして定義しておいてモジュールとして呼び出すやり方を使っています。
例えば、ディレクトリの存在チェック。指定のディレクトリがない場合のみ作成するようにする処理です。

def check_dir_exist(filepath):
if os.path.isfile(filepath):
pass:
else:
os.mkdir(filepath)

こういうときってPythonでは呼び出し側のスクリプトの頭に

#main.py
import check_dir_exist

呼び出される側のスクリプト名から拡張子を取り除いたファイル名を書くことでmain.pyで使えるようになります。

ところが、例えばサーバー(ホスト)相手に処理するときなどはログサーバーやメールサーバーなどで呼び出したいスクリプトが違ってくる場合があります。
たとえば以下のようなリストファイルが用意されている場合などです。

#list.txt
user1 host-mail pass1 common.py
user2 host-log  pass2 log.py

host-mailはcommon.pyで処理したい、
host-logはlog.pyで処理したい。

このようにケースバイケースで呼び出すモジュールを変えていくことを”動的にモジュールを呼び出す”なんて言います。

動的なモジュールの呼び出しにはimportlibを使う

動的にモジュールを呼び出すには標準ライブラリであるimportlibを使います。

条件

(1)呼び出される側のスクリプトにはクラス定義が存在しないこと
(2)呼び出される側のスクリプト名とメソッド名が同じであること
(3)メソッドは1つのスクリプトに1つのみ定義されていること
(4)呼び出されるスクリプトは呼び出す側のスクリプトと同階層のディレクトリに入れてあること(今回はbin/とします)。

ライブラリの呼び出し

#main.py
import importlib

読み込むモジュールを変数に格納

#main.py
#今回は/bin/common.pyを呼び出します
m = importlib.import_module('bin', + 'common')

モジュールの名前だけ取り出す

#main.py
module_name = m.__name__

メソッド名を取り出す

#main.py
func_name = 'common'

メソッドを使える上にする

#main.py
#getattr()を使います
method = getattr(m, func_name)

定義したメソッドに引数を渡して各処理をする

#main.py
method(引数1,…,n)

上記のような形式のリスト:list.txtを読み込んでそこに書かれたホスト名に対して同じく書かれたスクリプトを呼び出して処理させるループ文を書いてみます

#main.py
import os
import re
import importlib
LIST = "list.txt"
open_list = open(LIST, 'r')
for line in open_list:
info = re.split(" ", line)
USER_NAME = info[0]
HOST_NAME = info[1]
PASSWORD = info[2]
SCRIPT = info[3]
script_splitext, _ = os.path.splitext(SCRIPT)
m = importlib.import_module('bin.' + script_splitext)
module_name = m.__name__
func_name = str(script_splitext)
method = getattr(m, func_name)
method(USER_NAME, HOST_NAME, PASSWORD)

必要なスクリプトをbin/に入れてmain.pyを実行すればあとはリストに従ってやってくれます、便利便利