Pythonのnamespaceについて聞かれたので、おさらいとメモ
まずは、定義されているPEP
http://www.python.org/dev/peps/pep-0382/
namespaceの目的は別ディレクトリにあるパッケージ内のモジュールをそれぞれインポートできるようにすること。
具体的には、以下のような状況を考える。
aodag.hoge/aodag/__init__.py
aodag.hoge/aodag/hoge.py
aodag.fuga/aodag/fuga.py
aodag.fuga/aodag/__init__.py
といった構成があって、
PYTHON_PATH=aodag.hoge:aodag.fuga
とする。
で、pythonを実行してみると。
◯ import aodag
◯ import aodag.hoge
× import aodag.fuga
となる。
PYTHON_PATHの順番で、先にaodag.hogeディレクトリ以下のaodagパッケージが読み込まれてしまうので、aodag.fugaディレクトリのaodagパッケージが読み込まれない。
ここで aodag パッケージをnamespaceパッケージにすると、別ディレクトリにあるaodagパッケージ以下のモジュールもimportできるようになる。
それぞれのディレクトリの aodag/__init__.py 内にnamespece宣言を書く。
二種類方法があって、まずは、setuptools/distributeによる方法。
pkg_resources.declare_namespace(__name__)
次は、pkgutilを使う方法
__path__ = pkgutil.extend_path(__path__, __name__)
注意したいのは、どのaodagディレクトリでも __init__.py にnamespace宣言をしなきゃいけない。
なぜかというと、先にnamespace宣言のないパッケージがロードされてしまうと、他のディレクトリのパッケージがロードされないので、一番最初にロードされるパッケージにnamespace宣言が必要になる。そして、ロードの順序というのはインストールの順序や環境設定などで予測不能になってしまうからだ。
さて、これでどちらのディレクトリからもモジュールはロードできる。
上の例だと、aodag.hogeもaodag.fugaもロードできるってこと。
さて、同じaodag.*** のように扱えそうな物では、aodag/__init__.py モジュール(__init__ は名前なしモジュールみたいなものなので、import aodag といったようにパッケージ名でインポートする) の中に直接書いたオブジェクトがあり得る。
aodag/__init__.py で 関数funcを宣言したときに、from aodag import func ができるのだろうか?
これはできることもあるしできないこともある!?
なにを言っているのか?と思われそうだが、PYTHON_PATH=aodag.hoge:aodag.fuga ってなってるときは、aodag.hoge/aodag/__init__.py に書いた関数などは、インポート可能。
でも、aodag.fuga/aodag/__init__.py に書いた関数はインポート不可能。
ということでnamespace packageにしても、その直下の__init__.pyで宣言したオブジェクトは最初のパッケージのものしか使えない。
ようするにnamespace package以下には、モジュール以外のものを置いてはいけない。
さもなければ、環境毎にimportできたりできなかったりと不安定な状態になる。
あと、namespace packageの目的にあるように、別ディレクトリ間での同じパッケージをまとめるものであるので、全てのパッケージで、namespace宣言するのも間違いなんだね。
namespaceパッケージというのは、こういうもの。
feiz君、分かったかな?