68
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

東京理科大学Advent Calendar 2018

Day 23

Python で自作モジュールを扱うときに困ったこと

Last updated at Posted at 2018-12-22

はじめに

この記事は 東京理科大学 Advent Calendar 2018 23日目の記事です.
現在東京理科大学工学部情報工学科の3年になる自分が書いていきます.

今回書く内容は, Python でアプリケーションを開発する大学の課題 (なんなら明日プレゼン) に取り組んだときに直面したサブモジュールのお話です.
当初の予定では専攻分野でもあるので,機械学習関連について書きたいと思っていたんですが,少し他のことで忙しくて間に合わなかったので,他で投稿しようとしていた備忘録を投げときます.すいません.

モジュールの import

Python ではモジュールを呼び出すときには import ***from *** import *** と書きますが,自作モジュールを扱う際には少し注意が必要です.

解説のために以下のような構造で考えます.
(以降,サブパッケージのサブモジュールのことをサブモジュールと書きますのでご留意ください.)

.
├── root.py
└── sub_dir
    ├── sub.py
    └── subsub_dir
        └── subsub.py

メインモジュールからサブモジュールの呼び出し

ここで,root.py から sub.pysubsub.py を呼び出したいとします.
このときは簡単で,

root.py
from sub_dir import sub
from sub_dir.subsub_dir import subsub

とすれば呼び出せることは容易にわかると思います.

サブモジュールからサブモジュールの呼び出し

では,sub.py から subsub.py を呼び出し,それを root.py で呼び出したいときはどうすればよいでしょうか.

root.py
from sub_dir import sub
sub.py
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 を参照しようとして起きています.
ですので,

sub.py
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 を読み込みたいとします.
このときはモジュール同様に,

root.py
with open('./sub_dir/sub.txt') as f:
    print(f.read())

と,ごく一般的な文法で記述できます.

サブモジュールからテキストファイルの読み込み

では,sub.py から sub.txt を読み込み,それを root.py で呼び出したいときはどうすればよいでしょうか.

root.py
from sub_dir import sub
sub.py
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'

と,今度はファイルが見つからないというエラーが起きてしまいます.
先ほどと同じように,参照に関するエラーなのですが,今度は少し面倒な解決法になります.

sub.py
import os

with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'sub.txt')) as f:
    print(f.read())

これで root.py から sub.txtsub.py を介して読み込むことができました.
結局,相対的に読み込むのは難しいから絶対パスで参照しているだけなのですが,分解して解説したいと思います.

まず __file__ の部分ですが,これはカレントディレクトリから実行ファイルの相対パスが返ってきます.
これを os.path.dirname(__file__) とすることで,実行ファイルが存在するディレクトリの相対パスが返ってきます.
さらに,os.path.abspath() で呼び出すディレクトリが変わったときに対応するために,相対パスを絶対パスに変換しています.
最後に返ってきた絶対パスと,参照したいテキストファイル名を os.path.join で結合することで,メインモジュールから呼び出したモジュールを介して任意のディレクトリのテキストファイルを読み込むことができました.

まとめ

なんでテキストファイル読み込むのにこんなこと書かなきゃいけないのかって感じですが,意外と困ったので記事にしておきました.
何もテキストファイルに限定した話ではなく,keras.models.load_model() とか,PIL.Image.open() とか,意外と使うシーンがあるのかなと思います.

もっとスマートな方法あったら教えてください.

あと一週間で今年も終わりですし,もう一本くらい記事書けたらいいなって思ってますが,そろそろ参加している Kaggle のコンペが締め切りラッシュなので色々頑張りたいですね.

68
69
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?