【Python】1つ上の階層に存在するディレクトリ内のモジュールからimportする方法

Pythonで1つ上位の階層に存在するディレクトリ内のモジュールからimportする際に発生するエラーと対処法を具体的に説明いたします。

状況

以下のようなディレクトリ構成で、あなたがpkg_bにいるとします。

pkgs
├── pkg_a
│   └── module_a.py (pkg_b/module_b.pyで使用したいfunc_a()が定義されている)
└── pkg_b
    └── module_b.py

ここでpkg_b/module_b.pyからpkg_a/module_a.pyの関数func_a()をimportしたいとします。

(加えて、環境変数のPYTHONPATHや.pthファイルなどにインポート対象のパスを追加したくない状況だとします。)

想定されるエラー

pkg_b/module_b.pyのimport文で以下のような記述をすると、それぞれエラーが出る。

(ここではPython3.7.4を想定しています。)

from module_a import func_a

from module_a import func_aでimportしようとした場合に以下のようなエラーが発生する。

ModuleNotFoundError: No module named 'module_a'

from ..pkg_a.module_a import func_a

from ..pkg_a.module_a import func_aでimportしようとした場合に以下のようなエラーが発生する。

ValueError: attempted relative import beyond top-level package

解決法

pkg_b/module_b.pyで以下の記述をするとfunc_a()をimportできるようになる。

import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from pkg_a.module_a import func_a

この書き方だとpkg_bからだけではなくpkgsからmodule_b.pyを実行しても問題無くfunc_a()をimportできます。

余談ですが、この書き方だとPython2.7などのバージョンでは以下のようなエラーが発生します。

ImportError: No module named pkg_a.module_a

その場合の選択肢として、以下のような書き方があります。

import sys
sys.path.append('../pkg_a')
from module_a import func_a

'../pkg_a'でパスを追加しているので、pkgsからmodule_b.pyを実行しようとするとエラーが発生します。くれぐれもご注意ください。

参考

How to fix "Attempted relative import in non-package" even with __init__.py - Stack Overflow

コメント