以下は、例として適合するように変更された一般的なレシピです。パッケージとして作成されたPythonライブラリを処理するために現在使用しています。相互依存ファイルを含み、それらの一部を少しずつテストできるようにしたいと考えています。これlib.fooを呼び出してlib.fileA、関数f1とf2、およびlib.fileBクラスへのアクセスが必要であるとしましょうClass3。
これがprintどのように機能するかを説明するためにいくつかの呼び出しを含めました。実際には、それらを削除することをお勧めします(from __future__ import print_functionラインも削除する可能性があります)。
この特定の例は、エントリをに挿入する必要がある場合に表示するには単純すぎsys.pathます。(私たちが行うケースについてはラースの答えを参照、我々はパッケージディレクトリの2つ以上のレベルを持っている場合、それを必要とし、その後、我々が使用するos.path.dirname(os.path.dirname(__file__))ことが本当にありません-ブタ傷つけることもなく、これを行うには、安全なだけで十分ですどちらかここに。)if _i in sys.pathテスト。しかし、各インポートされたファイルが同じ挿入場合は、パスを-のインスタンス、両方の場合fileAおよびfileBパッケージ-このクラッタアップからのインポート・ユーティリティにしたいsys.path同じパスで何度も、持っていることの素敵なので、if _i not in sys.path定型文で。
from __future__ import print_function # only when showing how this works
if __package__:
print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
from .fileA import f1, f2
from .fileB import Class3
else:
print('Not a package; __name__ is {!r}'.format(__name__))
# these next steps should be used only with care and if needed
# (remove the sys.path manipulation for simple cases!)
import os, sys
_i = os.path.dirname(os.path.abspath(__file__))
if _i not in sys.path:
print('inserting {!r} into sys.path'.format(_i))
sys.path.insert(0, _i)
else:
print('{!r} is already in sys.path'.format(_i))
del _i # clean up global name space
from fileA import f1, f2
from fileB import Class3
... all the code as usual ...
if __name__ == '__main__':
import doctest, sys
ret = doctest.testmod()
sys.exit(0 if ret.failed == 0 else 1)
ここでの考え方はこれです(そして、これらはすべてpython2.7とpython 3.xで同じように機能することに注意してください):
として実行する場合 import libfrom lib import foo通常のコードからインポートまたは通常のパッケージインポートとして、__packageis libおよび__name__is lib.fooです。からのインポート.fileAなど、最初のコードパスを使用します。
として実行する場合python lib/foo.py、__package__Noneとに__name__なります__main__。
2番目のコードパスを使用します。libディレクトリはすでにになりsys.path、それを追加する必要はありませんので。fileA等から輸入しております。
libとしてディレクトリ内で実行した場合python foo.pyの動作は、ケース2の場合と同じです。
内で実行する場合 libとしてディレクトリpython -m fooの動作は、ケース2および3と同様です。ただし、libディレクトリへのパスがにないsys.pathため、インポートする前に追加します。Pythonを実行した後も同じことが当てはまりますimport foo。
(以来. であるにsys.path、私たちは本当にここで、パスの絶対的なバージョンを追加する必要はありません。私たちがやりたい、より深いパッケージの入れ子構造が、ここはfrom ..otherlib.fileC import ...、違いになります。あなたはこれをやっていない場合、あなたがすることができますすべてのsys.path操作を完全に省略します。)
ノート
まだ癖があります。このすべてを外部から実行する場合:
$ python2 lib.foo
または:
$ python3 lib.foo
動作はの内容によって異なりlib/__init__.pyます。それが存在し、空の場合、すべて正常です。
Package named 'lib'; __name__ is '__main__'
しかし、lib/__init__.py それ自体がインポートしroutineて、routine.name直接エクスポートできるようにする場合lib.name、、次のようになります。
$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'
つまり、モジュールは2回インポートされます。1回はパッケージを介してインポートされ、その後再び__main__実行されます。mainコードをます。Python 3.6以降ではこれについて警告します:
$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'
警告は新しいですが、警告し-について動作ではありません。これは、ダブルインポートトラップと呼ばれるものの一部です。(詳細については、問題27487を参照してください。)Nick Coghlan氏は次のように述べています。
この次のトラップは、3.3を含むすべての現在のバージョンのPythonに存在し、次の一般的なガイドラインにまとめることができます:「パッケージディレクトリ、またはパッケージ内のディレクトリは、Pythonパスに直接追加しないでください」。
注ここではそのルールに違反している間、我々はそれを行うことのみロードされているファイルがあるときではありませんパッケージの一部としてロードされている、と私たちの修正は、具体的に私たちは、そのパッケージ内の他のファイルへのアクセスを許可するように設計されています。(そして、すでに述べたように、単一レベルのパッケージではおそらくこれを行うべきではありません。)さらにクリーンにしたい場合は、次のように書き換えます。
import os, sys
_i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _i not in sys.path:
sys.path.insert(0, _i)
else:
_i = None
from sub.fileA import f1, f2
from sub.fileB import Class3
if _i:
sys.path.remove(_i)
del _i
つまりsys.path、インポートを実行するのに十分な時間を変更してから、元の状態に戻します(_iifのコピーを1つ追加した場合に限り、ifのコピーを1つ削除します_i)。