【簡単Python】exe化するスクリプトは相対パスのコーディング方法に注意|'__file__' と os.getcwd()の違い
いつもありがとうございます。
Python で作成したプログラムを 、Python がインストールされていないPCで実行するにはプログラムを "exe化"する必要があります。
Python で効率化ツールなどを作成しても、普通の会社では自分以外のオフィスワーカー全員に Python がインストールされていることは稀だと思いますので、必然的に exe化 して配る必要が生じます。
Pythonスクリプト(.py)を直接実行する場合と異なり、exe化した実行ファイルを実行すると、実行ファイルが一時ディレクトリ(Temp)に展開され、これが問題となる場合があります。
以下に例を示します。
- ディレクトリ構造
your_program
├ data ─ data.csv
├ src ─ module.py
├ main.py
└ main.exe
- コード
module.py (module.exe):
import os
# カレントディレクトリを取得
cur_dir = os.path.dirname(__file__)
print(cur_dir)
- 実行結果
これを想定せずにコードの中に何らかの相対パスの記述をしているとエラーが発生します。
実行ファイルが一時ディレクトリ(Tempディレクトリ)に展開されると想定していないため、相対パスの起点となるディレクトリを間違えているためです。
例えば以下のようなコードで、"main.exe" を実行すると "data" ディレクトリにアクセスできないためエラーが発生し、処理が中断されます。("main.py" は正常に動作します)
main.py(main.exe) :
# 自作モジュールをインポート
from src import module
# 自作モジュールを実行
module.show_file()
input('>>> press any key to shut down')
module.py :
def show_file():
# 標準ライブラリのインポート
import os
import glob
# カレントディレクトリの取得
cur_dir = os.path.dirname(__file__)
os.chdir(cur_dir)
# dataディレクトリにあるファイルを取得
file = glob.glob('../data/*')
# ファイル名を出力
print(os.path.basename(file[0]))
if __name__ == '__main__':
show_file()
本稿では、これの解決方法を解説します。
筆者の記事関連経験/資格
- Python 3 エンジニア認定基礎試験 合格
一般社団法人Pythonエンジニア育成推進協会によって運営・認定されている民間資格で、経済産業省が定めたガイドライン「ITスキル標準(ITSS)」において、職種:ソフトウェアディベロップメント、専門分野:応用ソフトのレベル1に掲載されています。 - Python 使用経験 約5年
【結論】os.getcwd()を使う
exe実行ファイルが一時ディレクトリ(Temp)に展開されてしまうことによる問題を解決するには "os.getcwd()
" を使う。
解説していきます。
os.getcwd() を使用した解決方法の解説
exe化した実行ファイルを実行した時に、'__file__' だと exe が展開された一時ディレクトリのパスを取得します。
一方で、os.getcwd()は exe が置いてあるディレクトリのパスを取得します。
これは '__file__'
が一時ディレクトリに展開されたファイルのパスを取得するのに対して、os.getcwd()
はexe実行時のカレントディレクトリを取得するためです。
os.getcwd() を使用したコードの具体例
以下のディレクトリ構造において、main.exe から module.py を呼び出して実行した場合でも、module.pyを単独で実行した場合でも、相対パス指定で data.csv にアクセスできるようにするには、以下のようにコードを記述します。
ただし、この方法はディレクトリのパスに”src”という文字列が1回しか出てこないことが前提です。
- ディレクトリ構造
your_program
├ data ─ data.csv
├ src ─ module.py
├ main.py
└ main.exe
- コード
main.exe (main.py) :
# 自作モジュールのインポート
from src import module
# 自作モジュールの実行
module.show_file()
input('>>>press any key to shut down') # 黒い画面(コンソール画面)を消さないための処理
module.py :
def show_file():
# 標準ライブラリのインポート
import os
import glob
# カレントディレクトリの取得
cur_dir = os.getcwd()
# カレントディレクトリが親ディレクトリなのか子ディレクトリなのかによって data への相対パスを変える
if 'src' in cur_dir:
path_str = r'../data/*'
else:
path_str = r'data/*'
# dataディレクトリにあるファイルを取得
file = glob.glob(path_str)
# ファイル名を出力
print(os.path.basename(file[0]))
if __name__ == '__main__':
show_file()
- 実行結果
コードを作成していく実際の場面では、モジュールごとに単体テストを行い、main.py で結合テストすることが多いと思います。モジュールテストを行う時と、結合テストを行う時とで、いちいちパスの記述を切り替えるのは非効率であり、ヒューマンエラーも起きやすいです。
そのため、モジュール単体で実行しても、main.py で実行してもエラーが起こらないようにしておくことは非常に重要です。
おわりに
ご覧いただきありがとうございました。
今回の記事では、Python スクリプト(.py)を exe 化して実行する際に、実行ファイルが一時ディレクトリ(Temp)に展開されてしまうことによる弊害への対処方法を解説いたしました。
当サイトのプログラミング解説記事は「わかりやすさ」を追求していますが、本稿は若干難しい内容になっていると感じます。より平易に説明できるように精進いたします。
お問い合わせやご要望等ございましたら、「お問い合わせ/ご要望」またはコメントにて、ご連絡いただければ幸いでございます。
皆様の人生がより一層素晴らしいものになるよう、少しでもお役に立てれば幸いでございます。
Python プログラミングスキルアップのための参考情報
ここでは参考図書を紹介いたしますが、これに限らず自分に合うものを選ぶことが重要だと考えております。皆様の、より一層のご成功を心からお祈りしております。
「独学プログラマー」というPythonを題材にした書籍は大変勉強になりました。Pythonの技術解説だけにとどまらず、プログラミングの魅力や基本的な知識、思考法、仕事の進め方まで幅広く学べます。
こちらの記事でも紹介しております。もしよろしければご覧ください。
投稿を編集 “【独学プログラマーにおすすめ】「独学プログラマー」|影響を受けた愛読書” ‹ みんなの実用学 — WordPress (jitsuyogaku.com)
また、Pythonに関する書籍も多数出版されています。興味のある方は、ぜひチェックしてみてください。
>>Amazon で Python の書籍をチェックする>>楽天市場で Python の書籍をチェックする