標準ライブラリでは、明確にドキュメント化されています ソースファイルを直接インポートする方法 (ソースファイルへの絶対ファイルパスを指定した場合) 。
暗黙の兄弟インポートが存在する場合に、この例をどのように適合させることができますか?
私はすでにトピックについて this および this other Stackoverflowの質問をチェックアウトしましたが、暗黙の兄弟インポートwithin手動でインポートされるファイル。
これが実例です
ディレクトリ構造:
_root/
- directory/
- app.py
- folder/
- implicit_sibling_import.py
- lib.py
_
_app.py
_:
_import os
import importlib.util
# construct absolute paths
root = os.path.abspath(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
isi_path = os.path.join(root, 'folder', 'implicit_sibling_import.py')
def path_import(absolute_path):
'''implementation taken from https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly'''
spec = importlib.util.spec_from_file_location(absolute_path, absolute_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
isi = path_import(isi_path)
print(isi.hello_wrapper())
_
_lib.py
_:
_def hello():
return 'world'
_
_implicit_sibling_import.py
_:
_import lib # this is the implicit sibling import. grabs root/folder/lib.py
def hello_wrapper():
return "ISI says: " + lib.hello()
#if __name__ == '__main__':
# print(hello_wrapper())
_
_python folder/implicit_sibling_import.py
_ブロックをコメントアウトして_if __name__ == '__main__':
_を実行すると、_ISI says: world
_ in Python 3.6。
ただし、_python directory/app.py
_を実行すると次の結果が得られます。
_Traceback (most recent call last):
File "directory/app.py", line 10, in <module>
spec.loader.exec_module(module)
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
File "/Users/pedro/test/folder/implicit_sibling_import.py", line 1, in <module>
import lib
ModuleNotFoundError: No module named 'lib'
_
import sys; sys.path.insert(0, os.path.dirname(isi_path))
を_app.py
_に追加すると、_python app.py
_は意図したとおりworld
を生成しますが、可能であれば_sys.path
_を変更しないでください。
_python app.py
_で_ISI says: world
_を出力し、_path_import
_関数を変更することでこれを達成したいと思います。
_sys.path
_をマングルすることの意味がわかりません。例えば。 _directory/requests.py
_があり、directory
へのパスを_sys.path
_に追加した場合、インポートする代わりに_import requests
_が_directory/requests.py
_のインポートを開始したくない 要求ライブラリ _pip install requests
_でインストールしたもの。
ソリューション[〜#〜] [[##〜]は、python目的のモジュールへのファイルパスで、 モジュールオブジェクト を返します。
理想的には、ソリューションは副作用を導入すべきではありません(たとえば、_sys.path
_を変更する場合、_sys.path
_を元の状態に戻す必要があります)。解決策が副作用を引き起こす場合、副作用を導入しないと解決策を達成できない理由を説明する必要があります。
PYTHONPATH
これを行う複数のプロジェクトがある場合、プロジェクトを切り替えるたびにPYTHONPATH
を設定することを覚えておく必要はありません。ユーザーは、プロジェクトを_pip install
_実行し、追加のセットアップなしで実行できる必要があります。
-m
__-m
_フラグ が推奨/ Pythonのアプローチですが、標準ライブラリには明確に文書化されています ソースファイルを直接インポートする方法 。暗黙の相対的なインポートに対処するために、このアプローチをどのように適応させることができるか知りたいです。明らかに、Pythonの内部はこれを行う必要があります。したがって、内部は「ソースファイルを直接インポートする」ドキュメントとどのように異なりますか?
PYTHONPATH
環境変数にアプリケーションのパスを追加します
モジュールファイルのデフォルトの検索パスを増やします。形式は、シェルのPATHと同じです。os.pathsepで区切られた1つ以上のディレクトリパス名(例:UnixのコロンまたはWindowsのセミコロン)。存在しないディレクトリは黙って無視されます。
bashでは次のようになります。
export PYTHONPATH="./folder/:${PYTHONPATH}"
または直接実行:
PYTHONPATH="./folder/:${PYTHONPATH}" python directory/app.py
OPのアイデアは素晴らしいです。この例では、sys.modulesに適切な名前の兄弟モジュールを追加することでのみ機能します。PYTHONPATHを追加するのと同じだと思います。テスト済みで、バージョン3.5.1で動作します。
import os
import sys
import importlib.util
class PathImport(object):
def get_module_name(self, absolute_path):
module_name = os.path.basename(absolute_path)
module_name = module_name.replace('.py', '')
return module_name
def add_sibling_modules(self, sibling_dirname):
for current, subdir, files in os.walk(sibling_dirname):
for file_py in files:
if not file_py.endswith('.py'):
continue
if file_py == '__init__.py':
continue
python_file = os.path.join(current, file_py)
(module, spec) = self.path_import(python_file)
sys.modules[spec.name] = module
def path_import(self, absolute_path):
module_name = self.get_module_name(absolute_path)
spec = importlib.util.spec_from_file_location(module_name, absolute_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return (module, spec)
def main():
pathImport = PathImport()
root = os.path.abspath(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
isi_path = os.path.join(root, 'folder', 'implicit_sibling_import.py')
sibling_dirname = os.path.dirname(isi_path)
pathImport.add_sibling_modules(sibling_dirname)
(lib, spec) = pathImport.path_import(isi_path)
print (lib.hello())
if __name__ == '__main__':
main()
試してください:
export PYTHONPATH="./folder/:${PYTHONPATH}"
または直接実行:
PYTHONPATH="./folder/:${PYTHONPATH}" python directory/app.py
ルートがPYTHONPATH
で明示的に検索されるフォルダーにあることを確認してください。絶対インポートを使用します。
from root.folder import implicit_sibling_import #called from app.py
絶対インポートを使用します。
root.folderからインポートimplicit_sibling_import#app.pyから呼び出される