見出し画像

pythonの自作クラスや自作モジュールのパス設定について

pythonのパス設定は少し特殊で、これまでエンジニアとしてjava等で開発を行ってきた人には多少違和感があると思いましたのでメモを残したいと思います。
例えば以下のような構成でpythonのコードを組んだとします。

main.py

from folder.a import a
a.a()

main.py

folder/a.py

from subfolder.b import b
class a:
 def a():
  b.b()
  print(’a’)

folder/a.py

folder/subfolder/b.py

class b:
 def b():
  print(’b’)

folder/subfolder/b.py

コードの内容としては、main.pyが、一つ下の階層にあるa.pyモジュールのaクラスをimportして、aクラスのaメソッドを呼ぶ。(メイン処理)
a.pyのaクラスは、一つ下の階層にあるb.pyモジュールのbクラスをimportして、bクラスのbメソッドを呼ぶ。

上記構成で、main.pyを実行するパスの問題でエラーになります。

folder\a.py", line 2, in <module>
    from subfolder.b import b
ModuleNotFoundError: No module named 'subfolder'

*a.pyで読み込んでいるsubfolderが見つからないと言われます。

これは、pythonのimportの仕組みによるものが原因で、恐らくpythonは単純にimoprtされたファイルを読み込んでいるだけだと思われます。
その為、main.pyを実行した場合の挙動は以下のようになると思われます。

main.py

from folder.a import a → 実行時にa.pyを読み込み展開する
—--- 展開されたa.py -----
from subfolder.b import b 
class a:
 def a():
  b.b()
  print(’a’)
-----------------------------

a.a()

main.py

この際、実行されたmain.pyはルートフォルダにいるので、そこからsubfolderが見つからない為、エラーになっています。
(ルートフォルダの直下にはfolderしかないので、当然と言えば当然になります。)

解決策として私が対応したのは以下の2パターンです。

  1. sys.path.appendでパスを追加する

  2. .pthファイルでパス問題を追加する


1.sys.path.appendでパスを追加する方法

main.pyの実行時にsubfolderが見つからないと怒られているので、folderにパスをsys.path.appendで追加する。

main.py

import sys
sys.path.append("folder")

from folder.a import a
a.a()

main.py

これで、folderにもパスが通る為、subfolderもmain.pyから見れるようになります。

2.pthファイルでパスを追加する

pythonのsite-packagesフォルダに.pthファイルを配置する。

例えば、.pthファイルを、mypath.pthとした場合、mypath.pthファイルにfolderのパスを記載して、pythonのsite-packagesフォルダに配置する。

 mypath.pth

c:\ルートフォルダ\folder

mypath.pth

*pythonのsite-packagesフォルダのパスは、以下のコマンドを実行すると確認できます。

⁠python -c "import site; print (site.getsitepackages())"

補足:上記以外にもPoetryを使用した方法がありますので、興味がある方は以下を参照してください。

https://qiita.com/siida36/items/b171922546e65b868679

まとめ

pythonの自作モジュールや自作クラスにパスを通す方法としては、私個人としては、2.の.pthファイルの対応を採用しています。

但し、上記2. の手順とは若干パスの通し方を変えていまして、folderではなく、ルートフォルダにパスを通しています。

 mypath.pth

c:\ルートフォルダ

mypath.pth

こうした場合、a.pyのソースファイルは次のようになります。

folder/a.py

from folder.subfolder.b import b
class a:
 def a():
  b.b()
  print(’a’)

folder/a.py

必ず、imoprtする際は、ルートフォルダからのパスを指定する形になります。

これの何がメリットかというと、subfolderの下にsubsubfolderを作成して、c.pyというファイルを配置し、b.pyから、c.pyをimportする場合も同じようにルートフォルダからのパスの指定で対応ができます。

folder/subfolder/b.py

from folder.subfolder.subsubfolder.c import c
class b:
 def b():
  print(’b’)

folder/subfolder/b.py

*前述の通り、必ずルートフォルダからimportする形式になり、ソース全体としてimportの記述が統一されます。

従来の方法だと.pthファイルにsubfolderのパスも追加する必要が出てきます。

 mypath.pth

c:\ルートフォルダ\folder
c:\ルートフォルダ\folder\subfolder

mypath.pth

システムが大きくなるにつれて、フォルダは拡張及び細分化していきますのでその都度上記パスファイルを弄るのは非常に面倒ですし、管理が煩雑になります。

最後に1.sys.path.appendの方法については、個人的にはお勧めしません。

理由としては、システムの規模が大きくなるにつれて、sys.path.appendがいろいろなソースコードに埋め込まれて、パスの管理が困難になるからです。

こうなってくると、importの順番で偶然パスが通る場合と、通らない場合が発生したりしてデバッグが大変になります。

また、ネットで調べたところsys.path.appendはコーディング規約違反らしいので、何れにせよ使わない方が良いのかもしれません。

■参考資料

この記事が気に入ったらサポートをしてみませんか?