【簡単Python】exe化するスクリプトは相対パスのコーディング方法に注意|'__file__' / os.getcwd()関数 / getattr関数
いつもありがとうございます。
「ノンプログラマー向けPython解説シリーズ」へようこそ。
Python で作成したプログラムを 、Python がインストールされていないPCで実行するにはプログラムを "exe化"する必要があります。
Python で効率化ツールなどを作成しても、普通の会社や研究室などでは自分以外の人全員のPCに Python がインストールされていることは稀だと思いますので、必然的に exe化 して配る必要が生じます。
Pythonスクリプト(.py)を直接実行する場合と異なり、exe化した実行ファイルを実行すると、実行ファイルが一時ディレクトリ(Temp)に展開され、これが問題となる場合があります。
以下に例を示します。
ディレクトリ構成
your_program/
├─ module.py
└─ module.exe
コード
import os
# カレントディレクトリを取得
cur_dir = os.path.dirname(__file__)
print(cur_dir)
input('>>> Press Enter Key to Shutdown')
実行結果
上述のコードを実行すると以下の結果が返ってきます。Pythonスクリプトと実行ファイル(.exe)とで、結果が異なります。
C:\Path\to\your_directory\your_program
>>> Press Enter Key to Shutdown
C:\Path\to\your_directory\Temp\_MEI87762 # 一時ディレクトリに展開されている
>>> Press Enter Key to Shutdown
.exe で実行した場合、カレントディレクトリが一時ディレクトリになっていることが分かります。
これを想定せずにコードの中に何らかの相対パスの記述をしているとエラーが発生します。相対パスの基点となるディレクトリが意図せず変わってしまうためです。
具体例を示します。以下のようなコードで、main.exe
を実行すると data
ディレクトリにアクセスできないためエラーが発生し、処理が中断されます。(main.py
は正常に動作します)
ディレクトリ構成
your_program/
├─ main.py
├─ main.exe
├─ src/
└─ module.py
└─ data/
└─ data.csv
コード
# 自作モジュールをインポート
from src import module
# 自作モジュールを実行
module.show_files()
input('>>> Press Enter Key to Shutdown')
def show_files():
# 標準ライブラリのインポート
import os
import glob
# カレントディレクトリの取得
cur_dir= os.path.dirname(os.path.abspath(__file__))
os.chdir(cur_dir)
# dataディレクトリにあるファイルを取得
file = glob.glob('../data/*')
# ファイル名を出力
print(os.path.basename(file[0]))
if __name__ == '__main__':
show_file()
実行結果
main.py
は正常に動作しますが、main.exe
はエラーが発生します。
data.csv
>>>press any key to shut down
Traceback (most recent call last):
File "main.py", line 3, in <module>
module.show_files()
File "src\module.py", line 9, in show_files
os.chdir(cur_dir)
FileNotFoundError: [WinError 2] 指定されたファイルが見つかりません。: 'C:\\Path\\to\\your_directory\\Temp\\_MEI6402\\src'
本稿では、これの解決方法を解説します。
解決方法
os.getcwd関数を使用する方法
os.getcwd()
は、現在の作業ディレクトリを返す関数です。
.exe
を実行したとき、__file__
はその.exe
が展開された一時ディレクトリのパスを返します。一方、os.getcwd()
は実行時の作業ディレクトリ、つまり.exe
が置かれているディレクトリのパスを返します。
以下に、具体例を用いて解説していきます。
ディレクトリ構成
your_program/
├─ main.py
├─ main.exe
├─ src/
└─ module.py
└─ data/
└─ data.csv
コード
# 自作モジュールのインポート
from src import module
# 自作モジュールの実行
module.show_files()
input('>>>Press Enter key to Shutdown')
# 標準ライブラリのインポート
import os
def show_files():
# 現在の作業ディレクトリを取得
cur_dir = os.getcwd()
# カレントディレクトリが 'src' で終わるかどうかをチェック
if cur_dir.endswith('src'):
# 'src' ディレクトリ内で実行されている場合、親ディレクトリの 'data' ディレクトリを参照
entries = os.listdir('../data')
else:
# それ以外の場合、カレントディレクトリ内の 'data' ディレクトリを参照
entries = os.listdir('data')
# 'data' ディレクトリ内の各ファイル名を出力
for entry in entries:
print(entry)
# スクリプトが直接実行された場合にのみ、show_files() を呼び出す
if __name__ == '__main__':
show_files()
実行結果
main.py
、main.exe
共に、正常に以下の結果が返ってきます。
data.csv
>>>Press Enter key to Shutdown
また、module.py
単体で実行しても以下の結果が返ってきます。このように、プログラムを作成する際は、module.py
単独で実行したときも、main.py (main.exe)
から実行した時も正常に動作させることが好ましいです。なぜならば、モジュール単体でテストを行うのに便利だからです。
data.csv
注意点
os.getcwd()
関数は、現在の作業ディレクトリを返す関数です。直接的に.exeを識別するものではありません。
以下で os.getcwd()
を使う方法の解説は終了です。ありがとうございました。
getattr関数を使用する方法(推奨)
getattr関数は、オブジェクトから指定した属性を文字列として取り出すことができる関数です。
基本構文は以下の通りです。
getattr(object, name[, default])
引数 | 解説 |
---|---|
object | 属性を取得したい対象のオブジェクトを指定します。 |
name | 取得したい属性の名前を文字列で指定します。 |
default(省略可能) | 属性が存在しない場合に返されるデフォルト値を指定します。この引数を指定しない場合、属性が存在しないとAttributeError が発生します。 |
以下に、具体例を用いて解説していきます。
ディレクトリ構成
your_program/
├─ main.py
├─ main.exe
├─ src/
└─ module.py
└─ data/
└─ data.csv
コード
# 自作モジュールのインポート
from src import module
# 自作モジュールの実行
module.show_files()
input('>>>Press Enter key to Shutdown')
# 標準ライブラリのインポート
import os
import sys
def show_files():
# プログラムが実行ファイル(Frozenバンドル)として実行されている場合の処理
if getattr(sys, 'frozen', False):
# 実行ファイルのディレクトリを取得
cur_dir = os.path.dirname(sys.executable)
# 'src'ディレクトリに移動
os.chdir(os.path.join(cur_dir, 'src'))
else:
# スクリプトファイルのディレクトリを取得
cur_dir = os.path.dirname(os.path.abspath(__file__))
# そのディレクトリに移動
os.chdir(cur_dir)
# '../data'ディレクトリ内のエントリ(ファイルやフォルダ)を取得
entries = os.listdir('../data')
# 各エントリ(フォルダ内のファイルやサブフォルダ)を表示
for entry in entries:
print(entry)
if __name__ == '__main__':
show_files()
コード実行結果
main.py
、main.exe
共に、正常に以下の結果が返ってきます。
data.csv
>>>Press Enter key to Shutdown
また、module.py
単体で実行しても以下の結果が返ってきます。上述のセクションの解説と繰り返しになりますが、このように、module.py
単独で実行したときも、main.py (main.exe)
から実行した時も正常に動作させることが好ましいです。なぜならば、モジュール単体でテストを行うのに便利だからです。
data.csv
以上でgetattr
を使う方法の解説は終了です。ありがとうございました。
おわりに
ご覧いただきありがとうございました。
今回の記事では、Python スクリプト(.py)を exe 化して実行する際に、実行ファイルが一時ディレクトリ(Temp)に展開されてしまうことによる弊害への対処方法を解説いたしました。
当サイトのプログラミング解説記事は「わかりやすさ」を追求していますが、本稿は若干難しい内容になっていると感じます。より平易に説明できるように精進いたします。
お問い合わせやご要望等ございましたら、「お問い合わせ/ご要望」またはコメントにて、ご連絡いただければ幸いでございます。
皆様の人生がより一層素晴らしいものになるよう、少しでもお役に立てれば幸いでございます。
尚、当サイトでは様々な情報を発信しております。もしよろしければ、トップページもご覧いただけると幸いでございます。
記事関連経験
- Python 3 エンジニア認定基礎試験
経済産業省が定めたガイドライン「ITスキル標準(ITSS)」に掲載されている民間資格です。 - Python 使用経験 約5年
実務に使用する アプリを多数作成してきました。
Python プログラミングスキルアップのための参考情報
ここでは参考図書を紹介いたしますが、これに限らず自分に合うものを選ぶことが重要だと考えております。皆様の、より一層のご成功を心からお祈りしております。
「独学プログラマー」というPythonを題材にした書籍は大変勉強になりました。Pythonの技術解説だけにとどまらず、プログラミングの魅力や基本的な知識、思考法、仕事の進め方まで幅広く学べます。
こちらの記事でも紹介しております。もしよろしければご覧ください。
また、Pythonに関する書籍は多数出版されています。興味のある方は、チェックしてみてください。
\チェックしてみよう/
\チェックしてみよう/
\チェックしてみよう/