web-dev-qa-db-ja.com

__path__は何に役立ちますか?

今日まで、一部のパッケージで定義されている__path__属性に気づかなかった。ドキュメントによると:

パッケージは、もう1つの特別な属性__path__をサポートしています。これは、そのファイルのコードが実行される前に、パッケージの__init__.pyを保持するディレクトリの名前を含むリストに初期化されます。この変数は変更できます。これにより、パッケージに含まれるモジュールおよびサブパッケージの今後の検索に影響します。

この機能は多くの場合必要ありませんが、パッケージにあるモジュールのセットを拡張するために使用できます。

誰かが私にこれが正確に何を意味するのか、なぜ私がそれを使いたいのか説明してくれませんか?

56
Jason Baker

これは通常、パッケージをディスク全体に配置するために pkgutil とともに使用されます。たとえば、zope.interfaceとzope.schemaは別々のディストリビューションです(zopeは「名前空間パッケージ」です)。 _/usr/lib/python2.6/site-packages/zope/interface/_でzope.schemaをローカルで使用しているときに、_/home/me/src/myproject/lib/python2.6/site-packages/zope/schema_にzope.interfaceがインストールされている可能性があります。

_/usr/lib/python2.6/site-packages/zope/__init__.py_にpkgutil.extend_path(__path__, __name__)を配置すると、pkgutilで___path___が_['/usr/lib/python2.6/site-packages/zope', '/home/me/src/myproject/lib/python2.6/site-packages/zope']_に変更されるため、zope.interfaceとzope.schemaの両方がインポート可能になります。

_pkg_resources.declare_namespace_(Setuptoolsの一部)は_pkgutil.extend_path_に似ていますが、パス上のzipをより認識しています。

手動で___path___を変更することは一般的ではなく、おそらく必要ではありませんが、名前空間パッケージのインポート問題をデバッグするときに変数を確認することは役立ちます。

サルパッチングに___path___を使用することもできます。たとえば、_distutils/__init__.py_の初期のファイル_sys.path_を作成して、distutilsにサルパッチを適用することがあります。

_import os
stdlib_dir = os.path.dirname(os.__file__)
real_distutils_path = os.path.join(stdlib_dir, 'distutils')
__path__.append(real_distutils_path)
execfile(os.path.join(real_distutils_path, '__init__.py'))
# and then apply some monkeypatching here...
_
30
Ian Bicking

__path__を変更すると、インタープリターに、そのパッケージに属するモジュールを別のディレクトリで探すように強制できます。

これにより、たとえば、ランタイム条件に基づいて、同じモジュールの異なるバージョンをロードできます。異なるプラットフォームで同じ機能の異なる実装を使用したい場合は、これを行うことがあります。

31
Syntactic

構文 が示すように、ランタイム条件に基づいてモジュールの異なるバージョンを選択することに加えて、この機能により、単一の論理の外観を維持しながら、パッケージを複数の部分/ダウンロード/インストールに分割することもできますパッケージ。

以下を検討してください。

  • mypkg_mypkg_fooの2つのパッケージがあります。
  • _mypkg_fooには、mypkgfoo.pyへのオプションモジュールが含まれています。
  • ダウンロードしてインストールしたとき、mypkgにはfoo.pyが含まれていません。

mypkg__init__.pyは次のように実行できます。

try:
    import _mypkg_foo
    __path__.append(os.path.abspath(os.path.dirname(_mypkg_foo.__file__)))
    import mypkg.foo
except ImportError:
    pass

誰かがパッケージ_mypkg_fooをインストールしている場合、mypkg.fooを利用できます。存在しない場合は存在しません。

7
Matt Anderson

私が遭遇した特定の状況は、パッケージが大きくなり、それを参照するコードを変更せずに、パッケージの一部をサブディレクトリに分割したい場合です。

たとえば、私はviewsというパッケージを使用しています。このパッケージは、パッケージの主要なトップレベルの目的と混同されていたいくつかのサポートユーティリティ関数を収集していました。これらのサポート関数をサブディレクトリutilsに移動し、viewsパッケージの__init__.pyに次の行を追加できました。

__path__.append(os.path.join(os.path.dirname(__file__), "utils"))

この変更もviews/__init_.pyで、ファイルをさらに変更することなく、残りのソフトウェアを新しいファイル構造で実行できました。

views/__init__.pyファイルのimportステートメントで同様のことを実行しようとしましたが、viewパッケージのインポートを通じてサブパッケージモジュールがまだ表示されていません-私は私がそこに何か足りないのかどうかは完全にはわかりません。その歓迎についてのコメント!)

(Python 2.7インストールに基づくこの応答)

0
Graham Klyne