Pythonのnamespace

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君、分かったかな?