文字列で指定しているモジュールを import する

前回のセッションバックエンドについて書いた。 Djangoのセッションバックエンドを調べる(file, db) - そのあれ

そのとき、 django の設定ファイルでは文字列でパスを指定している。ただ、文字列をパスで指定しているだけでどう読み込んでいるかまでよく知らなかった。

セッションバックエンドの指定では django/middleware.py at master · django/django · GitHub で使われている。

結論

__import__ をつかうか importlib.import_module をつかう。

あまり詳しく調べてないけど、特に理由がなければ importlib.import_module のほうが標準ライブラリなのでいいんじゃないかなという感じがする。実際、 import_module 関数の中ではキャッシュやロックの管理のようななにかが書かれている。

検証

リポジトリはこちら altnight/djsession at importlib · GitHub

こういう構成になっている。

$ cat app_a/__init__.py
print('init %s' % __file__)
$ cat hello.py
print('hello %s' % __file__)

__init__.pyhello.py という import すると自身のファイルパスを表示するだけのモジュール。

$ tree app_a
app_a
├── __init__.py
├── hello.py
└── level1
    ├── __init__.py
    ├── hello.py
    └── level2
        ├── __init__.py
        └── hello.py

2 directories, 6 files

ためしにつかう main.py はこういう内容。

  • import_module でモジュール指定
  • __import__ でモジュール指定
  • import_module でパッケージ指定
  • __import__ でパッケージ指定
from importlib import import_module

import_module('hello')
import_module('app_a.hello')
import_module('app_a.level1.hello')
import_module('app_a.level1.level2.hello')

#__import__('hello')
#__import__('app_a.hello')
#__import__('app_a.level1.hello')
#__import__('app_a.level1.level2.hello')

#import_module('app_a')
#import_module('app_a.level1')
#import_module('app_a.level1.level2')

#__import__('app_a')
#__import__('app_a.level1')
#__import__('app_a.level1.level2')

結果

$ python main.py
hello /Users/altnight/u/src/djsession/djsession/hello.py
init /Users/altnight/u/src/djsession/djsession/app_a/__init__.py
hello /Users/altnight/u/src/djsession/djsession/app_a/hello.py
init /Users/altnight/u/src/djsession/djsession/app_a/level1/__init__.py
hello /Users/altnight/u/src/djsession/djsession/app_a/level1/hello.py
init /Users/altnight/u/src/djsession/djsession/app_a/level1/level2/__init__.py
hello /Users/altnight/u/src/djsession/djsession/app_a/level1/level2/hello.py

ちなみにパッケージじゃなくても(=ディレクトリに init.py がなくても) import できる。