【Python】指定PathをRootにしてimportする

投稿日:2021/01/24

はじめに

今回の記事は,私個人の「この書き方,個人的に書きやすいと思うけどどうなんかな~,ちょっと課題あると思うけど自分のためのメモとして残しとこ」という感じのものです.
そのためPython公式の推奨でも一般的に使用されているやり方でもないので,参考程度に読んでいただけたらと思います.

今回は,Pythonのimportについて個人的に引っかかっていて,それに対して対応したコードをメモります.
というのは,Pythonでのimportにおいて「TypeScriptのような指定Path(例えばプロジェクトディレクトリとか)をRootにして,そのPathからの絶対パスでimportする」ようなことができない状況だと思います.
(でもできたりする時がある?イマイチつかめない…)

"__init__.py"を使って対応する方法があると思うのですが,いかんせんなかなか理解が進まず…
CLIとかパッケージングしてコマンドで実行させた時にディレクトリ構成を認識してくれるようなもんかなぁと雑に理解しちゃってます.つまり,"python main.py"のように純粋にPythonスクリプトを実行させた際にそのディレクトリ構成を見てくれていないのかな?と思っちゃっていますね….

我流import術

サンプルディレクトリ
<プロジェクト>/ ├ src/ ├ main.py ├ modules/ ├ moduleA/ ├ a.py ├ moduleB/ ├ b.py ├ test/ ├ testA.py

上記のサンプルディレクトリで下記2点のPythonスクリプトを雑に書いてみます.
●「a.pyのfuncA()がb.pyのfuncB()を呼び出す」
●「testA.pyがa.pyのfuncA()を呼び出す」

main.py
import inspect import os import sys PYPATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + "/" sys.path.append(PYPATH + ".") from modules.moduleA.a import funcA if __name__ == "__main__": funcA()
a.py
# import inspect # import os # import sys # PYPATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + "/" from modules.b import funcB def funcA(): print("Hello, A!") funcB()
b.py
# import inspect # import os # import sys # PYPATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + "/" # importするものは特に無いので記載せず def funcB(): print("Hello, B!")
testA.py
import inspect import os import sys PYPATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + "/" sys.path.append(PYPATH + "./../") from modules.moduleA.a import funcA funcA()

とまぁ,こんな感じのコードで,「指定Path(例えばプロジェクトディレクトリとか)をRootにして,そのPathからの絶対パスでimportする」,今回のサンプルコードでいうと,「"src"をRootとして"modules"からのPathでimportする」とことができてるのかなぁと思ってます.
ポイントとして下記のコードがpythonコマンドで実行するPythonスクリプト(今回のサンプルでは"main.py"と"testA.py")の頭に実装されています.

指定PathをRootにするコード
import inspect import os import sys PYPATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + "/" sys.path.append(PYPATH + "(Rootとしたいディレクトリ(今回のsrc)までの相対パス)")

モジュール"inspect"・"os"の関数の組み合わせにより,Pythonスクリプト自体のカレントディレクトリを取得しており,そのディレクトリからの相対Pathで「(プロジェクトディレクトリのような)Rootとしたいディレクトリ」を取得し,"sys.path"というモジュール検索Pathの一覧に追加します.
したがって上記のコードによりimportのPathをすべてRootとしたディレクトリ下から辿れるようになります.
さらに,PYPATHに各Pythonスクリプトのカレントディレクトリを格納できるので,"os.getcwd()"や"__file__"などといったことに頭を悩ませる必要もなくなります(ぇ).

難点としては,pythonコマンドで実行する全てのPythonスクリプトに上記コードをコピペしないといけないかな~というくらいでしょうか.
可能なら引数として相対パスを渡す形で,上記コードを1つの関数にして使い回せれば良いのですが,その関数自体のimportがまず厳しいかもしれないということで,ひとまずコピペで落ち着いてます.

終わりに

以上です.あまり強く参考にしないでください(逃)

タグ:

Comment

コメントはありません。
There's no comment.