はじめに
この記事は 東京理科大学 Advent Calendar 2018 23日目の記事です.
現在東京理科大学工学部情報工学科の3年になる自分が書いていきます.
今回書く内容は, Python でアプリケーションを開発する大学の課題 (なんなら明日プレゼン) に取り組んだときに直面したサブモジュールのお話です.
当初の予定では専攻分野でもあるので,機械学習関連について書きたいと思っていたんですが,少し他のことで忙しくて間に合わなかったので,他で投稿しようとしていた備忘録を投げときます.すいません.
モジュールの import
Python ではモジュールを呼び出すときには import ***
や from *** import ***
と書きますが,自作モジュールを扱う際には少し注意が必要です.
解説のために以下のような構造で考えます.
(以降,サブパッケージのサブモジュールのことをサブモジュールと書きますのでご留意ください.)
.
├── root.py
└── sub_dir
├── sub.py
└── subsub_dir
└── subsub.py
メインモジュールからサブモジュールの呼び出し
ここで,root.py
から sub.py
と subsub.py
を呼び出したいとします.
このときは簡単で,
from sub_dir import sub
from sub_dir.subsub_dir import subsub
とすれば呼び出せることは容易にわかると思います.
サブモジュールからサブモジュールの呼び出し
では,sub.py
から subsub.py
を呼び出し,それを root.py
で呼び出したいときはどうすればよいでしょうか.
from sub_dir import sub
from subsub_dir import subsub
と最初は書きたくなりますが,これで root.py
を実行すると,
Traceback (most recent call last):
File "root.py", line 1, in <module>
from sub_dir.sub import sub_func
File "/home/skcvim/tmp/root/sub_dir/sub.py", line 1, in <module>
from subsub_dir import subsub
ModuleNotFoundError: No module named 'subsub_dir'
と,モジュールが見つからないというエラーが起きます.
これは,内部的には実行しているディレクトリから相対的に from subsub_dir import subsub
を参照しようとして起きています.
ですので,
from .subsub_dir import subsub
このように,明示的に相対 import を記述してあげると,エラーなく読み込むことができます.
パッケージ内参照
https://docs.python.jp/3/tutorial/modules.html#intra-package-references
ファイルの読み込み
ここから少し話が変わって,以下のようなディレクトリ構造で考えてみます.
.
├── root.py
└── sub_dir
├── sub.py
└── sub.txt
メインモジュールからテキストファイルの読み込み
まず,root.py
から sub.txt
を読み込みたいとします.
このときはモジュール同様に,
with open('./sub_dir/sub.txt') as f:
print(f.read())
と,ごく一般的な文法で記述できます.
サブモジュールからテキストファイルの読み込み
では,sub.py
から sub.txt
を読み込み,それを root.py
で呼び出したいときはどうすればよいでしょうか.
from sub_dir import sub
with open('./sub.txt') as f:
print(f.read())
と書いてしまうと,モジュール同様に,
Traceback (most recent call last):
File "root.py", line 1, in <module>
from sub_dir import sub
File "/home/skcvim/tmp/root/sub_dir/sub.py", line 1, in <module>
with open('./sub.txt') as f:
FileNotFoundError: [Errno 2] No such file or directory: './sub.txt'
と,今度はファイルが見つからないというエラーが起きてしまいます.
先ほどと同じように,参照に関するエラーなのですが,今度は少し面倒な解決法になります.
import os
with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'sub.txt')) as f:
print(f.read())
これで root.py
から sub.txt
を sub.py
を介して読み込むことができました.
結局,相対的に読み込むのは難しいから絶対パスで参照しているだけなのですが,分解して解説したいと思います.
まず __file__
の部分ですが,これはカレントディレクトリから実行ファイルの相対パスが返ってきます.
これを os.path.dirname(__file__)
とすることで,実行ファイルが存在するディレクトリの相対パスが返ってきます.
さらに,os.path.abspath()
で呼び出すディレクトリが変わったときに対応するために,相対パスを絶対パスに変換しています.
最後に返ってきた絶対パスと,参照したいテキストファイル名を os.path.join
で結合することで,メインモジュールから呼び出したモジュールを介して任意のディレクトリのテキストファイルを読み込むことができました.
まとめ
なんでテキストファイル読み込むのにこんなこと書かなきゃいけないのかって感じですが,意外と困ったので記事にしておきました.
何もテキストファイルに限定した話ではなく,keras.models.load_model()
とか,PIL.Image.open()
とか,意外と使うシーンがあるのかなと思います.
もっとスマートな方法あったら教えてください.
あと一週間で今年も終わりですし,もう一本くらい記事書けたらいいなって思ってますが,そろそろ参加している Kaggle のコンペが締め切りラッシュなので色々頑張りたいですね.