Pythonのパスの書き方
pythonコマンドを使ってプログラムを実行した場合のインポートやファイル参照のパスについて、理解できていなかったので、実際に動かして動作を確認しました。
なお、相対パスはpythonコマンドで実行するモジュールでは使えないという制約があります。
fromの指定
ディレクトリ構造
project_root/
|-- main.py
|-- child_dir/
|-- sub_main.py
|-- child_module.py
|-- child_module2.py
|-- grandchild_dir/
|-- grandchild_module.py
ファイルの内容
from child_dir.child_module import ChildModule
child = ChildModule()
child()
child.child()
# import ChildModule
child = ChildModule()
child()
child.child()
# import ChildModuleTwo
# import GrandchildModule
class ChildModule:
def __call__(self):
print('ChildModule')
#;
def child(self):
child = GrandchildModule()
child()
brother = ChildModuleTwo()
brother()
#;
#;
class ChildModuleTwo:
def __call__(self):
print('ChildModuleTwo')
#;
#;
class GrandchildModule:
def __call__(self):
print('GrandchildModule')
#;
#;
main.py実行時
project_rootにて、python3 main.py
を実行する。
この時、child_module.pyのimport
文が以下だと、エラーになる。
from child_module2 import ChildModuleTwo
# エラー内容
ModuleNotFoundError: No module named 'child_module2'
from grandchild_dir.grandchild_module import GrandchildModule
# エラー内容
ModuleNotFoundError: No module named 'grandchild_dir'
次のようにchild_module.pyから見た相対パスまたは、main.pyから見たパスでgrandchild_module
を記載すると動作する。
# child_module.pyから見た相対パス
from .child_module2 import ChildModuleTwo
from .grandchild_dir.grandchild_module import GrandchildModule
# main.pyから見たパス
from child_dir.child_module2 import ChildModuleTwo
from child_dir.grandchild_dir.grandchild_module import GrandchildModule
結果
ChildModule
GrandchildModule
ChildModuleTwo
child_dir/sub_main.py実行時
次にproject_rootにて、python3 child_dir/sub_main.py
を実行する。
sub_main.pyのimport文
この時、sub_main.pyのimport
文が以下だと、エラーになる。
# 実行ディレクトリから見たパス
from child_dir.child_module import ChildModule
# エラー内容
ModuleNotFoundError: No module named 'child_dir'
# sub_main.pyから見た相対パス
from .child_module import ChildModule
# エラー内容
ImportError: attempted relative import with no known parent package
次のように、実行するモジュールからのパスだと正常に動作する。
from child_module import ChildModule
child_module.pyのimport文
この時、child_module.pyのimport
文が以下だと、エラーになる。
# 相対パス
from .grandchild_dir.grandchild_module import GrandchildModule
# エラー内容
ImportError: attempted relative import with no known parent package
# 実行ディレクトリから見たパス
from child_dir.grandchild_dir.grandchild_module import GrandchildModule
# エラー内容
ModuleNotFoundError: No module named 'child_dir'
次のように、実行するモジュールからのパスだと正常に動作する。
from grandchild_dir.grandchild_module import GrandchildModule
結果
ChildModule
GrandchildModule
sub_main.py実行時
次にchild_dirにて、python3 sub_main.py
を実行する。
この時の結果はproject_rootでpython3 child_dir/sub_main.py
実行時と同じだった。
結論
import
文はpythonコマンドで実行するモジュールからのパスで記載するか、import
されたファイルから見た相対パスを記載する。import
されたファイルと同一ディレクトリにあるモジュールの相対パスを記載時はfrom .パッケージ名 import モジュール名
となる。
pythonコマンドで実行するモジュールだけでなく、実行するモジュールと同じディレクトリにあるモジュールをimportした場合も、そのファイル内で相対パスは使えない。
ファイルパスの指定
ディレクトリ構造
project_root/
|-- main.py
|-- sample.txt
|-- child_dir/
|-- sub_main.py
|-- child_module.py
|-- sample2.txt
|-- grandchild_dir/
|-- sample3.txt
|-- grandchild_module.py
from __future__ import with_statement
from child_dir.child_module import ChildModule
file_path = 'sample.txt'
with open(file_path, 'r') as f_in:
file_contents = f_in.read()
#;
print(file_contents)
child = ChildModule()
child.read()
child.read_child()
from child_module import ChildModule
file_path = 'sample2.txt'
with open(file_path, 'r') as f_in:
file_contents = f_in.read()
#;
print(file_contents)
child = ChildModule()
child.read()
child.read_child()
from __future__ import with_statement
from .grandchild_dir.grandchild_module import GrandchildModule
file_path = 'sample2.txt'
class ChildModule:
def read(self):
with open(file_path, 'r') as f_in:
file_contents = f_in.read()
#;
print(file_contents)
#;
def read_child(self):
child = GrandchildModule()
child.read()
#;
#;
file_path = 'sample3.txt'
class GrandchildModule:
def read(self):
with open(file_path, 'r') as f_in:
file_contents = f_in.read()
#;
print(file_contents)
#;
#;
main.py実行時
project_rootにて、python3 main.py
を実行する。
main.pyのfile_path
は以下のどちらでも正常に動作する。
file_path = 'sample.txt'
file_path = './sample.txt'
この時、child_module.pyのfile_path
は以下だとエラーになる。
file_path = 'sample2.txt'
file_path = './sample2.txt'
次のように、main.pyから見たパスだと正常に動作する。
file_path = 'child_dir/sample2.txt'
file_path = './child_dir/sample2.txt'
grandchild_module.pyのfile_path
は以下だとエラーになる。
file_path = 'sample3.txt'
file_path = './sample3.txt'
file_path = 'grandchild_dir/sample3.txt'
file_path = './grandchild_dir/sample3.txt'
次のように、main.pyから見たパスだと正常に動作する。
file_path = 'child_dir/grandchild_dir/sample3.txt'
file_path = './child_dir/grandchild_dir/sample3.txt'
child_dir/sub_main.py実行時
次にproject_rootにて、python3 child_dir/sub_main.py
を実行する。
この時、sub_main.pyのfile_path
が以下だと、エラーになる。
file_path = 'sample2.txt'
file_path = './sample2.txt'
次のように、pythonコマンドを実行したディレクトリ(project_root)から見たパスだと正常に動作する。
file_path = 'child_dir/sample2.txt'
file_path = './child_dir/sample2.txt'
同様にchild_module.pyのfile_path
もpythonコマンドを実行したディレクトリ(project_root)から見たパスだと正常に動作する。
grandchild_module.pyのfile_path
も以下だとエラーになる。
file_path = 'sample3.txt'
file_path = './sample3.txt'
file_path = 'grandchild_dir/sample3.txt'
file_path = './grandchild_dir/sample3.txt'
次のように、pythonコマンドを実行したディレクトリ(project_root)から見たパスだと正常に動作する。
file_path = 'child_dir/grandchild_dir/sample3.txt'
file_path = './child_dir/grandchild_dir/sample3.txt'
sub_main.py実行時
次にchild_dirにて、python3 sub_main.py
を実行する。
この時、sub_main.pyのfile_path
が以下だと、エラーになる。
file_path = 'child_dir/sample2.txt'
file_path = './child_dir/sample2.txt'
次のように、sub_main.pyから見たパスだと正常に動作する。
file_path = 'sample2.txt'
file_path = './sample2.txt'
同様にchild_module.pyのfile_path
もpythonコマンドを実行したディレクトリ(child_dir)から見たパスだと正常に動作する。
grandchild_module.pyのfile_path
は以下だとエラーになる。
file_path = 'child_dir/grandchild_dir/sample3.txt'
file_path = './child_dir/grandchild_dir/sample3.txt'
file_path = 'sample3.txt'
file_path = './sample3.txt'
次のように、pythonコマンドを実行したディレクトリ(child_dir)から見たパスだと正常に動作する。
file_path = 'grandchild_dir/sample3.txt'
file_path = './grandchild_dir/sample3.txt'
結論
FastAPI
ディレクトリ構成
project_root/
|-- main.py
|-- child_dir/
|-- sub_main.py
|-- child_module.py
|-- grandchild_dir/
|-- grandchild_module.py
ファイルの内容
from fastapi import FastAPI
from child_dir.child_module import ChildModule
app = FastAPI()
@app.get("/")
def get_root():
child = ChildModule()
child()
child.child()
return { "message": 'ok' }
#;
project_root/main.pyでは、相対パスのimport
文だとエラーになる。
from .child_dir.child_module import ChildModule
from fastapi import FastAPI
# import ChildModule
app = FastAPI()
@app.get("/")
def get_root():
child = ChildModule()
child()
child.child()
return { "message": 'ok' }
#;
# import ChildModuleTwo
# import GrandchildModule
class ChildModule:
def __call__(self):
print('ChildModule')
#;
def child(self):
child = GrandchildModule()
child()
brother = ChildModuleTwo()
brother()
#;
#;
class GrandchildModule:
def __call__(self):
print('GrandchildModule')
#;
#;
main.py実行時
project_rootにて、uvicorn main:app --host 0.0.0.0 --port 8000
を実行する。
この時、child_module.pyのimport
文が以下だと、エラーになる。
from child_module2 import ChildModuleTwo
from grandchild_dir.grandchild_module import GrandchildModule
次のいずれかであれば、import
できる。(通常のpython実行時と同じ)
from .child_module2 import ChildModuleTwo
from .grandchild_dir.grandchild_module import GrandchildModule
from child_dir.child_module2 import ChildModuleTwo
from child_dir.grandchild_dir.grandchild_module import GrandchildModule
child_dir/sub_main.py実行時
次にproject_rootにて、uvicorn child_dir.sub_main:app --host 0.0.0.0 --port 8000
を実行する。
この時、sub_main.pyのimport
文が以下だと、エラーになる。
# 実行ファイルから見たパス
from child_module import ChildModule
実行ディレクトリから見たパスまたは、実行ファイルから見た相対パスだとimport
できる。
from child_dir.child_module import ChildModule
from .child_module import ChildModule
import
されたファイルでは、実行ファイルから見たパスではimport
できない。また、import
されたファイルが、
- 実行ファイルと同じディレクトリの場合、実行ディレクトリからのパスで
import
文を記述できる - 実行ファイルのサブディレクトリの場合、実行ディレクトリからのパスまたは、対象ファイルからの相対パスで
import
文を記述できる
sub_main.py実行時
次にchild_dirにて、python3 sub_main.py
を実行する。
この時、sub_main.pyのimport
文が実行ファイルからの相対パスだと、エラーになる。
from .child_module import ChildModule
実行ディレクトリ(=実行ファイル)から見たパスだとimport
できる。
from child_module import ChildModule
import
されたファイルでは、
- 実行ディレクトリと同じディレクトリ(=実行ファイルと同じディレクトリ)の場合、実行ディレクトリからのパスで
import
文を記述できる - サブディレクトリの場合、実行ディレクトリからのパスまたは、対象ファイルからの相対パスで
import
文を記述できる
結論
uvicorn
実行時はサブディレクトリに実行ファイルを配置することで、全て相対パスでの記述が可能になる。