Pythonの相対インポートで上位ディレクトリ・サブディレクトリを指定
パッケージを自作する場合、パッケージ内のモジュールから上位ディレクトリ(親ディレクトリ)や下位ディレクトリ(サブディレクトリ)にあるほかのモジュール(ファイル)をインポートしたいことがある。
そのようなときは、相対パスで相対的な位置を指定してインポート(相対インポート)することができる。
パッケージ内のファイルのように、他のファイルからモジュールとしてインポートされるファイルの中では相対パスを使えるが、python
コマンドなどで実行されるファイルの中では相対パスは使えないので注意。
ここでは、まず、パッケージ内のモジュールから別ディレクトリのモジュールをインポートする方法について説明する。
- 自作パッケージの例
- パッケージ内で別ディレクトリからインポート
- 同じパッケージ(同じディレクトリ)からインポート
- サブパッケージ(サブディレクトリ)からインポート
- 上位パッケージ(上位ディレクトリ)からインポート
- 結果の例
- 相対インポートしているモジュールを単体で実行
次に、パッケージ外のファイル、python
コマンドなどで直接実行されるファイル(メインファイル)から別ディレクトリにあるモジュールやパッケージをインポートする方法について説明する。メインファイルというのは便宜的に呼んでいるだけで、特に一般的な名称ではない(たぶん)。
- メインファイルで別ディレクトリからインポート
- 同じディレクトリからインポート
- 下位ディレクトリからインポート
- 上位ディレクトリ(親ディレクトリ)からインポート
- 相対インポート
- 絶対インポート
- モジュール探索パスを追加して絶対インポート
なお、自作のパッケージやモジュールを標準ライブラリやpipでインストールしたサードパーティライブラリのように使いまわしたい場合は、環境変数PYTHONPATH
でモジュール探索パスを追加するのが便利。以下の記事を参照。
importの基本については以下の記事を参照。
自作パッケージの例
以下の構造のパッケージmy_package
を例とする。__init__.py
はすべて空ファイル。
my_package/
├── __init__.py
├── mod1.py
├── mod2.py
├── sub_package1
│ ├── __init__.py
│ └── sub_mod1.py
└── sub_package2
├── __init__.py
└── sub_mod2.py
各ファイルの中身を示す。説明は後述。
mod1.py
。
def func():
print('-- mod1.func is called')
mod2.py
。
from . import mod1
from .sub_package1 import sub_mod1
def func_same():
print('from mod2')
mod1.func()
def func_sub():
print('from mod2')
sub_mod1.func()
if __name__ == '__main__':
func_sub()
sub_package1/sub_mod1.py
。
def func():
print('-- sub_mod1.func1 is called')
sub_package2/sub_mod2.py
。
from .. import mod1
from ..sub_package1 import sub_mod1
def func_parent():
print('from sub_mod2')
mod1.func()
def func_parent_sub():
print('from sub_mod2')
sub_mod1.func()
パッケージ内で別ディレクトリからインポート
同じパッケージ(同じディレクトリ)からインポート
.
(ピリオド1つ)が同じディレクトリを表す。
mod2.py
から同じ階層のモジュールmod1.py
をインポートする場合、以下のように書く。
from . import mod1
サブパッケージ(サブディレクトリ)からインポート
下の階層のパッケージ(サブパッケージ)からモジュールをインポートする場合、同じ階層を示す.
に続けてパッケージ名(ディレクトリ名)を書く。
mod2.py
からsub_package1/sub_mod1.py
をインポートする場合、以下のように書く。
from .sub_package1 import sub_mod1
上位パッケージ(上位ディレクトリ)からインポート
..
(ピリオド2つ)が1つ上のディレクトリ(親ディレクトリ)を表す。
sub_package2/sub_mod2.py
から上位パッケージ(上位ディレクトリ)にあるmod1.py
をインポートする場合、以下のように書く。
from .. import mod1
sub_package2/sub_mod2.py
からsub_package1/sub_mod1.py
をインポートする場合、以下のように書く。
from ..sub_package1 import sub_mod1
なお、...
(ピリオド3つ)はさらに上の階層(2階層上)となり、.
を増やすとさらに上の階層を表すことが可能。
結果の例
my_package
をインポートした結果の例を示す。
各モジュールから別ディレクトリのモジュールがインポートされ関数が呼び出せていることが分かる。
from my_package import mod2
from my_package.sub_package2 import sub_mod2
mod2.func_same()
# from mod2
# -- mod1.func is called
mod2.func_sub()
# from mod2
# -- sub_mod1.func1 is called
sub_mod2.func_parent()
# from sub_mod2
# -- mod1.func is called
sub_mod2.func_parent_sub()
# from sub_mod2
# -- sub_mod1.func1 is called
モジュール内の関数などのオブジェクトを直接インポートすることも可能。
from my_package.mod2 import func_same, func_sub
from my_package.sub_package2.sub_mod2 import func_parent, func_parent_sub
func_same()
# from mod2
# -- mod1.func is called
func_sub()
# from mod2
# -- sub_mod1.func1 is called
func_parent()
# from sub_mod2
# -- mod1.func is called
func_parent_sub()
# from sub_mod2
# -- sub_mod1.func1 is called
なお、相対インポートではpython
コマンドで実行したファイルより上の階層には遡れない。
例えば、my_package
内にsub_package2/sub_mod2.py
をインポートするmain.py
を作成した場合。
from sub_package2 import sub_mod2
sub_mod2.func_parent()
これを実行するとエラーになってしまう。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook/my_package
python3 main.py
# Traceback (most recent call last):
# File "main.py", line 1, in <module>
# from sub_package2 import sub_mod2
# File "/Users/mbp/Documents/my-project/python-snippets/notebook/my_package/sub_package2/sub_mod2.py", line 1, in <module>
# from .. import mod1
# ValueError: attempted relative import beyond top-level package
エラーメッセージを読むと、sub_package2/sub_mod2.py
はインポートできているが、sub_mod2.py
の中で上位ディレクトリからmod1.py
を相対インポートしている部分でエラーが発生していることが分かる。
相対インポートしているモジュールを単体で実行
テストなどの目的で、上述のパッケージmy_package
内のモジュールmod2.py
を単体で実行したい場合、python
(環境によってはpython3
)コマンドで実行するとエラーとなる。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook
python3 my_package/mod2.py
# Traceback (most recent call last):
# File "my_package/mod2.py", line 1, in <module>
# from . import mod1
# ImportError: cannot import name 'mod1' from '__main__' (my_package/mod2.py)
-m
オプションをつけてモジュールとして実行するとOK。my_package/mod2.py
ではなくmy_package.mod2
のように指定する。
python3 -m my_package.mod2
# from mod2
# -- sub_mod1.func1 is called
このとき、カレントディレクトリがmy_package
を含むディレクトリ(my_package
の一つ上の階層)でないとエラーになるので注意。
cd my_package
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook/my_package
python3 -m mod2
# Traceback (most recent call last):
# File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/runpy.py", line 193, in _run_module_as_main
# "__main__", mod_spec)
# File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/runpy.py", line 85, in _run_code
# exec(code, run_globals)
# File "/Users/mbp/Documents/my-project/python-snippets/notebook/my_package/mod2.py", line 1, in <module>
# from . import mod1
# ImportError: attempted relative import with no known parent package
メインファイルで別ディレクトリからインポート
これまでの例のようにパッケージ内のモジュール(ファイル)ではなく、パッケージ外のスクリプトファイル、python
コマンドなどで直接実行されるファイルから別ディレクトリのモジュールやパッケージをインポートする場合について説明する。
以下のような構成を例とする。
dir_import_test/
├── dir
│ ├── main_absolute.py
│ ├── main_relative.py
│ └── main_sys_path_append.py
├── dir_for_mod
│ └── mod2.py
├── main_base.py
└── mod1.py
同じディレクトリからインポート
main_base.py
からmod1.py
をインポートする例。同じディレクトリにあるモジュール(ファイル)は特別な指定は必要なくそのままインポートできる。
モジュールをインポート。
import mod1
mod1.func()
# -- mod1.func is called
モジュール内の関数をインポート。
from mod1 import func
func()
# -- mod1.func is called
下位ディレクトリからインポート
main_base.py
からdir_for_mod/mod2.py
をインポートする例。Python3.3以降では、__init__.py
を含まないディレクトリもパッケージとしてインポートできるようになった。3.2以前はdir_for_mod
に__init__.py
がないとエラーになるので注意。
モジュールをインポート。
import dir_for_mod.mod2
dir_for_mod.mod2.func()
# -- dir_for_mod.mod2.func is called
from dir_for_mod import mod2
mod2.func()
# -- dir_for_mod.mod2.func is called
モジュール内の関数をインポート。
from dir_for_mod.mod2 import func
func()
# -- dir_for_mod.mod2.func is called
上位ディレクトリ(親ディレクトリ)からインポート
dir
内にあるスクリプトファイルから上位ディレクトリ(親ディレクトリ)のmod1.py
やdir_for_mod/mod2.py
をインポートする場合。
以下に説明するように、上位ディレクトリからインポートするのは面倒。強い理由がなければインポートしたいモジュールやパッケージと同じ階層または上の階層にメインファイルを置いたほうが楽。
また、冒頭でも書いたように、自作のパッケージやモジュールを標準ライブラリやpipでインストールしたサードパーティライブラリのように使いまわしたい場合は、環境変数PYTHONPATH
でモジュール探索パスを追加するのが便利。以下の記事を参照。
相対インポート
相対インポートはパッケージ内の仕組みであり、パッケージ外のスクリプトファイル、つまりpython
コマンドで実行するようなスクリプトファイルでは使えない。
相対 import は現在のモジュール名をベースにすることに注意してください。メインモジュールの名前は常に
__main__
なので、Python アプリケーションのメインモジュールとして利用されることを意図しているモジュールでは絶対 import を利用するべきです。
6. モジュール (module) パッケージ内参照 — Python 3.6.5 ドキュメント
以下のdir/main_relative.py
を例とする。
from .. import mod1
from ..dir_for_mod import mod2
mod1.func()
mod2.func()
これをpython
コマンドで実行するとエラー。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir_import_test
python3 dir/main_relative.py
# Traceback (most recent call last):
# File "dir/main_relative.py", line 1, in <module>
# from .. import mod1
# ValueError: attempted relative import beyond top-level package
上述のように、上の階層のディレクトリからpython
コマンドに-m
オプションを付けてモジュールとして実行すると動作する。
cd ..
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook
python3 -m dir_import_test.dir.main_relative
# -- mod1.func is called
# -- dir_for_mod.mod2.func is called
絶対インポート
以下のdir/main_absolute.py
を例とする。
import mod1
from dir_for_mod import mod2
mod1.func()
mod2.func()
importの対象ディレクトリ(モジュール探索パス)にはカレントディレクトリ(作業ディレクトリ=Pythonを起動したディレクトリ)が含まれる。したがって、カレントディレクトリがmod1.py
およびdir_for_mod
を含むディレクトリであればインポートできる。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir_import_test
python3 dir/main_absolute.py
# -- mod1.func is called
# -- dir_for_mod.mod2.func is called
カレントディレクトリが異なるとエラーとなってしまう。
cd dir
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir_import_test/dir
python3 main_absolute.py
# Traceback (most recent call last):
# File "main_absolute.py", line 1, in <module>
# import mod1
# ModuleNotFoundError: No module named 'mod1'
モジュール探索パスを追加して絶対インポート
スクリプトファイルのパスを__file__
で取得し、それを基準に親ディレクトリをimportの対象ディレクトリ(モジュール探索パス)に追加すると、カレントディレクトリによらず正しく処理される。
以下のdir/main_sys_path_append.py
を例とする。モジュール探索パスを追加してから親ディレクトリのファイルを絶対インポートする。
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import mod1
from dir_for_mod import mod2
mod1.func()
mod2.func()
カレントディレクトリによらず正しく処理される。
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir_import_test
python3 dir/main_sys_path_append.py
# -- mod1.func is called
# -- dir_for_mod.mod2.func is called
cd dir
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir_import_test/dir
python3 main_sys_path_append.py
# -- mod1.func is called
# -- dir_for_mod.mod2.func is called
cd ../..
pwd
# /Users/mbp/Documents/my-project/python-snippets/notebook
python3 dir_import_test/dir/main_sys_path_append.py
# -- mod1.func is called
# -- dir_for_mod.mod2.func is called