質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.93%

pythonのユーティリティモジュール同士でのimportが上手くいきません...

受付中

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 4,628

score 36

いつもお世話になっております。
pythonのimport文について理解ができず、困っております。

以下にフォルダ構成や、どんなエラーが出るかを記載します。
pythonに詳しい方、是非ご助言いただけないでしょうか...?

 ユースケース

社内用のpythonスクリプトを作成しており、
どのスクリプトからでも使われるような、
ユーティリティモジュール群を、複数ファイルに分割して作成しています。

このユーティリティモジュール群を、
ユーティリティモジュール同士でimportしたり、
別スクリプトからimportする際に、上手くいかずに困っています。

 フォルダ構成

  • 以下にフォルダ構成を示します。
  • bootstrap.py は初期化用のスクリプトです。utilities以下のモジュールをimportします。
  • utilitiesフォルダ以下のモジュール群は、相互にimportを行っています。
/lib
 ├ __init__.py
 ├ bootstrap.py
 └ /utilities
    ├ __init__.py
    ├ config_util.py
    ├ dataframe_util.py
    ├ datetime_util.py
    ├ env_util.py
    ├ file_util.py
    ├ http_util.py
    └  query_util.py

 具体例とエラー

  • bootstrap.py と各モジュールでは、冒頭で以下のようなimportを行っています。
#bootstrap.pyの冒頭部分
from utilities import query_util
from utilities import config_util
...
#query_util.pyの冒頭部分
from datetime_util import get_unixtime_from_str
...
  • 実行結果は以下のようになります。
#実行結果
python3 lib/utilities/query_util.py 
-> このように、各モジュールを単体で起動した場合は、特にエラーになりません

python3 lib/bootstrap.py
-> query_utilのimport時に、相対パス解決が上手くいかないのでエラーが発生します。
-> 実際のエラーを以下に示します。
#発生するエラー
  File "lib/bootstrap.py", line 61, in <module>
    from utilities import query_util
  File "/Applications/MAMP/htdocs/jupyter-to-td/lib/utilities/query_util.py", line 9, in <module>
    from datetime_util import get_unixtime_from_str
ImportError: No module named 'datetime_util'

 望んでいる状態

  • bootstrap.pyや他スクリプトからユーティリティモジュールのimportを行った際に、エラーが出ないようにしたい。
  • 各ユーティリティモジュールを単体で起動した場合にも、エラーにならないようにしたい。
  • (色々なパターンを試しましたが、「bootstrapからは各モジュールがimport出来るようになったものの、今度は各モジュール単体起動がダメになったり」と、上手くいきませんでしたorz)

 聞きたいこと

  • 以下について知りたいです。

  • 上記エラーを解決する方法。

  • python import文に関する良記事(以下のような観点について書いてあるもの)。

  1. ライブラリとして使う、モジュール間同士でimportを行う方法
  2. 下位のディレクトリから、上位ディレクトリに置いてあるライブラリをimportする方法
  • pythonのimport文を書く際のベストプラクティス
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • coco_bauer

    2016/08/11 17:32

    カレントフォルダーや実行しているPythonがあるフォルダー以外では、環境変数「PYTHONPATH」に列挙したフォルダとsys.pathに登録してあるフォルダからimportされます。 "lib/utilities/"をPYTHONPATHに付け加える、sys.pathに登録するのいずれかで解決しそうに思えます。
    sys.pathに登録してあるフォルダ

    キャンセル

  • th1209

    2016/08/11 17:58

    coco_bauerさん

    教えて頂きありがとうございます。
    そうですね、現在はimportが上手くいっていないので、各pythonファイルにsys.path.appendを行っている状態です。
    環境変数PYTHONPATHについては知りませんでした...。
    初回起動時に必ず呼ぶスクリプトに、上記環境変数の代入処理を仕込んで、各pythonスクリプトでimportするようにしようかと思います!

    キャンセル

回答 1

+1

私の知ってる範囲でのやり方なのでもっといい方法はあるかもしれませんが、

python 3では相対importがあるので、それを用います。

lib/bootstrap.py

from .utilities import query_util
from .utilities import config_util

def bs_some():
  query_util.something()

lib/utilities/query_util.py

from .datetime_util import get_unixtime_from_str 
def something():
  get_unixtime_from_str()

lib/utilities/datetime_util.py

def get_unixtime_from_str():
    print('works!')

main.py

from lib import bootstrap

bootstrap.bs_some()

この場合 python3 main.py は上手く動作すると思います。

ただし、python3 lib/bootstrap.py は、相対importでエラーとなってしまいます。

SystemError: Parent module '' not loaded, cannot perform relative import

これを回避するには

python3 -m lib.bootstrap
python3 -m lib.utilities.query_util


の様に実行してあげてください。

以下の様にしても一応回避はできますが、個人的にはこのやり方は使わないかなと感じてます。

lib/bootstrap.py

if __name__ == '__main__':
    from utilities import query_util
    from utilities import config_util
else:
    from .utilities import query_util
    from .utilities import config_util

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る