目的
要するに、こういうことを実施したいわけ。
+ dirA + packageName - moduleA.py + dirB + packageName - moduleB.py
上記のような構成、つまり、別ディレクトリに packageName という共通パッケージ空間があることを前提として、
import packageName.moduleA import packageName.moduleB
を成功させたい。どういうケースかというと、dirA 配下のファイル群と dirB 配下のファイル群は別配布だが、一方を他方のモジュールのアドオンとして提供する、というような形態を想定しているケースになる。
テスト
まず、下記のような構成を作る
% tree . . ├── dirA │ └── packageName │ ├── __init__.py │ └── moduleA.py ├── dirB │ └── packageName │ ├── __init__.py │ └── moduleB.py └── importtest.py 4 directories, 5 files
実験用スクリプト importtest.py として、以下を作成。
import sys print(sys.version) import packageName.moduleA import packageName.moduleB
これを実行すると、packageName.moduleB の import でエラーとなる。ぐわーマジか。ぼくは Python newbee なわけですが、この問題に最初に気付いたとき、Python マジで??これできないの???????と驚愕した。
$ PYTHONPATH=dirA:dirB python importtest.py 3.3.2 (default, Nov 6 2013, 01:40:08) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] Traceback (most recent call last): File "importtest.py", line 6, in <module> import packageName.moduleB ImportError: No module named 'packageName.moduleB'
解決策
できないわけはないらしく、調べていくと PEP 420 の Implicit Namespace Packages を利用すると楽という結論に達した。ミソは、__init__.py の削除になる。
$ find . -name __init__.py | xargs rm $ PYTHONPATH=dirA:dirB:$PYTHONPATH python importtest.py 3.3.2 (default, Nov 6 2013, 01:40:08) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)]
何が起こっているのか
PEP 420 に "Implicit" と書いてある通り、これは Namespace Package と呼ばれる Python の仕様を暗黙的に利用しているらしい。では Namespace Package とは何なのか。これは Python のドキュメントに記載がある。
名前空間パッケージは様々な ポーション を寄せ集めたもので、それぞれのポーションはサブパッケージを親パッケージに提供します。 ポーションはファイルシステムの別々の場所にあることもあります。 ポーションは、 zip ファイルの中やネットワーク上や、それ以外のインポート時に Python が探すどこかの場所で見付かることもあります。 名前空間パッケージはファイルシステム上のオブジェクトに対応することもあるし、そうでないこともあります; それらは実際の実体の無い仮想モジュールです。
http://docs.python.jp/3.3/reference/import.html#namespace-packages
何言っているのか分かんねぇ。
とにかくこういうときは実際に動かして見れば良い。
% PYTHONPATH=dirA:dirB:$PYTHONPATH python Python 3.3.2 (default, Nov 6 2013, 01:40:08) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> import packageName >>> print(packageName.__path__) _NamespacePath(['/Users/kiririmode/work/python/dirA/packageName', '/Users/kiririmode/work/python/dirB/packageName'])
このように、"packageName" というパッケージに関連付けられた __path__ には iterable なオブジェクトが設定されていて、その要素は個々のファイルパスになっている。モジュール検索パスとして iterable が使用されることにより、dirA 配下のモジュールであっても dirB 配下のモジュールであっても解決できるようになっているらしい。注目すべきは、PYTHONPATH に指定したただそれだけで、Python 実行系が dirA 配下の packageName と dirB 配下の packageName を見つけていることだろう。
さらに、sys.modules の中を覗くと、namespace になっていることが分かる。
>>> print(sys.modules['packageName']) <module 'packageName' (namespace)>