以下は、例として適合するように変更された一般的なレシピです。パッケージとして作成された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 lib
from lib import foo
通常のコードからインポートまたは通常のパッケージインポートとして、__package
is 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
、インポートを実行するのに十分な時間を変更してから、元の状態に戻します(_i
ifのコピーを1つ追加した場合に限り、ifのコピーを1つ削除します_i
)。