web-dev-qa-db-ja.com

Pythonで名前空間パッケージを作成するにはどうすればよいですか?

Pythonでは、名前空間パッケージを使用してPythonコードを複数のプロジェクトに分散できます。これは、関連ライブラリを個別のダウンロードとしてリリースする場合に便利です。たとえば、Package-1PYTHONPATHPackage-2

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py

エンドユーザーはimport namespace.module1およびimport namespace.module2を使用できます。

複数のPython製品がその名前空間でモジュールを定義できるように、名前空間パッケージを定義する最良の方法は何ですか?

128
joeforker

TL; DR:

Python 3.3では、何もする必要はありません。名前空間パッケージディレクトリに__init__.pyを入れないでください。それが機能するのは3.3です。 pkgutil.extend_path()ソリューションに対するpkg_resources.declare_namespace()ソリューション。これは将来の使用に耐え、暗黙のネームスペースパッケージと既に互換性があるためです。


Python 3.3では、暗黙の名前空間パッケージが導入されています。 PEP 42 を参照してください。

これは、import fooによって作成できる3つのタイプのオブジェクトがあることを意味します。

  • foo.pyファイルで表されるモジュール
  • __init__.pyファイルを含むディレクトリfooで表される通常のパッケージ
  • __init__.pyファイルなしの1つ以上のディレクトリfooで表される名前空間パッケージ

パッケージもモジュールですが、ここでは「モジュール」と言うときは「非パッケージモジュール」を意味します。

まず、sys.pathをスキャンしてモジュールまたは通常のパッケージを探します。成功すると、検索を停止し、モジュールまたはパッケージを作成して初期化します。モジュールまたは通常のパッケージが見つからなかったが、少なくとも1つのディレクトリが見つかった場合、名前空間パッケージを作成して初期化します。

モジュールと通常のパッケージには、__file__が作成元の.pyファイルに設定されています。通常パッケージと名前空間パッケージには、__path__setが作成元のディレクトリに設定されています。

import foo.barを実行すると、上記の検索が最初にfooに対して行われ、次にパッケージが見つかった場合、barの検索は代わりに検索パスとしてfoo.__path__を使用して行われますsys.pathの。 foo.barが見つかった場合、fooおよびfoo.barが作成および初期化されます。

では、通常のパッケージと名前空間パッケージはどのように混在しますか?通常はそうではありませんが、古いpkgutil明示的な名前空間パッケージメソッドは、暗黙的な名前空間パッケージを含むように拡張されています。

次のような__init__.pyを持つ既存の通常のパッケージがある場合:

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

...従来の動作は、検索パス上の他のregularパッケージを__path__に追加することです。ただし、Python 3.3では、名前空間パッケージも追加されます。

したがって、次のディレクトリ構造を使用できます。

├── path1
│   └── package
│       ├── __init__.py
│       └── foo.py
├── path2
│   └── package
│       └── bar.py
└── path3
    └── package
        ├── __init__.py
        └── baz.py

...そして2つの__init__.pyextend_path行がある限り(およびpath1path2およびpath3sys.pathにある場合) import package.fooimport package.bar、およびimport package.bazはすべて機能します。

pkg_resources.declare_namespace(__name__)は、暗黙の名前空間パッケージを含むように更新されていません。

63
clacke

pkgutil と呼ばれる標準モジュールがあり、これを使用して特定の名前空間にモジュールを「追加」できます。

指定したディレクトリ構造で:

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py

これらの2行をPackage-1/namespace/__init__.pyPackage-2/namespace/__init__.py(*)の両方に配置する必要があります。

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

(*-依存関係を指定しない限り-最初に認識されるのはどれか分からないため-詳細は PEP 42 を参照)

ドキュメント のように:

これにより、パッケージの__path__パッケージにちなんだ名前のsys.path上のディレクトリのすべてのサブディレクトリが追加されます。

これからは、これらの2つのパッケージを個別に配布できるようになります。

78
Mike Hordecki

このセクションは一目瞭然です。

要するに、名前空間のコードを__init__.pyに入れ、setup.pyを更新して名前空間を宣言すれば、自由に行くことができます。

5
iElectric

これは古い質問ですが、最近誰かが私のブログで名前空間パッケージについての私の投稿がまだ関連しているとコメントしたので、それを実行する方法の実用的な例を提供するので、ここにリンクすると思いました:

https://web.archive.org/web/20150425043954/http://cdent.tumblr.com/post/216241761/python-namespace-packages-for-tiddlyweb

これは、何が起こっているかの主な根源についてのこの記事へのリンクです。

http://www.siafoo.net/article/77#multiple-distributions-one-virtual-package

__import__("pkg_resources").declare_namespace(__name__)トリックは、 TiddlyWeb のプラグインの管理を促進するものであり、これまでのところうまく機能しているようです。

2
cdent