[[どぶお/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