[[どぶお/Pythonで遊ぼう!]]
* 論理パッケージを複数のパッケージに分ける [#z95a19bb]
** どういうこと? [#p53d03d0]
モジュールを作成している時に、一つの論理パッケージを複数のディレクトリで構成したい時があります。例えば、biokids.ccp4utilモジュールとbiokids.xdsutilモジュールを開発しているけど、これらはbiokidsパッケージである、という場合です。ディレクトリは以下のような構成になります。
lib1/
biokids/
__init__.py <-- biokidsモジュール
lib2/
biokids/
__init__.py <-- 空の__init__
ccp4util.py <-- biokids.ccp4utilモジュール
lib3/
biokids/
__init__.py <-- 空の__init__
xdsutil.py <-- biokids.xdsutilモジュール
一つのディレクトリで構成すればいいのですが、別々にバージョン管理をしたいとか、まだ開発途中であるとか理由はいろいろあると思います。~
さて、こんな時はどのようにすれば適切に読み込めるのでしょう?
** 読み込むためには [#wb0536c6]
まず、サーチパスについて考えてみましょう。PYTHONPATHを以下のようにすれば読み込めそうです。
PYTHONPATH=lib3:lib2:lib1
ところが、この場合、biokidsモジュールは''lib3/''で見つかってしまうので、''lib2/''は検索に行かず、''biokids.ccp4util''モジュールが見つからない、となってしまいます…。この問題は以下コードを''lib3/biokids/__init__.py''に記述することで解決されます。
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
- 参考 pkgutil -- http://docs.python.jp/2/library/pkgutil.html
動作としては以下のようになります。
+ import biokids.ccp4utilをしたい
+ biokidsモジュールを検索
+ lib3で発見!
+ lib3/biokids.pyを読み込む
+ この時extend_path()でモジュールサーチパス''__path__''にlib2/biokidsおよびlib1/biokidsが追加される
+ biokids.ccp4utilを検索
+ biokids.__path__に従ってlib3, lib2, lib1の順に検索
+ lib2/biokids/ccp4util.pyを発見
+ biokids.ccp4utilを読み込み
これでディレクトリが分散されていても一つのパッケージのように振る舞うことができます。
** __init__.pyは? [#p0adcb45]
ところがもう一つ問題があります。''import biokids''がうまく動作しません。biokidsモジュールはlib3/biokids/__init__.pyなのでlib1/biokids/__init__.pyを読み込まないのです。これを解決するには少々トリッキーな方法が必要になります。上記のextend_pathと組み合わせます。
lib3/biokids/__init__.pyつまりモジュール検索で一番最初にヒットするbiokids/__init__.pyを以下の内容にします。
import sys, pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
for dirname in __path__[1:]:
importer = pkgutil.ImpImporter(dirname)
mod = importer.find_module("").load_module("")
for key in dir(mod):
if key in ("__builtins__", "__doc__", "__file__", "__name__", "__package__", "__path__"): continue
setattr(sys.modules[__name__], key, getattr(mod, key))
これは、extend_path()でまず__path__を拡張し、自分を含まないパス一覧(__path__[1:])から__init__.pyを読み込んで、内容を自分自身(sys.modules[__name__])に追加する、という動作をします。~
もっとスマートな方法があるかもしれませんが、とりあえずこれで望む動作ができました。
** コメント [#v60bcbda]
コメントなどありましたらどうぞ。
コメントなどありましたらよろしくお願いします。
#comment