以前の記事を書いたとき、うまく動作しないPythonスクリプトがあった。
ファイル名の取得方法が誤っているのだろうと推定し、修正することで狙い通りの動作をするようになった。
その不具合・正常動作の理由を実験で確認したのでメモしておく。
要点
実験
Python 3.8.10 の場合
Python 3.8.10 1つ上のディレクトリから実行
Python 3.8.10 まとめ
Python 3.10.6 の場合
Python 3.10.6 1つ上のディレクトリから実行
Python 3.10.6 まとめ
パス文字列の結合
おまけ1
おまけ2
参考
要点
-
os.getcwd()
か__file__
でパスを取得。 -
os.path.join()
でパスを結合。 -
__file__
の仕様がPython3.8以前・Python3.9以降で変更されている。
実験
以下のスクリプトを実行し、出力結果を比較する。
import os
print(os.getcwd())
print(__file__)
print(os.path.dirname(__file__))
Python 3.8.10 の場合
以前の記事を書いた時点で入っていたPythonのバージョンがこれだった。
> python C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
> python get_names.py
C:\Users\(略)\test
get_names.py
(※空行が出力される)
> python .\get_names.py
C:\Users\(略)\test
.\get_names.py
.
いかにも不具合の種になりそうな差異が見られる。
Python 3.8.10 1つ上のディレクトリから実行
別ディレクトリから実行するとどうなるか。
> python C:\Users\(略)\test\get_names.py
C:\Users\(略)
C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
> python test\get_names.py
C:\Users\(略)
test\get_names.py
test
> python .\test\get_names.py
C:\Users\(略)
.\test\get_names.py
.\test
Python 3.8.10 まとめ
-
os.getcwd()
は実行時のカレントディレクトリを取得。 -
__file__
には実行時に指定した文字列がそのまま格納されている。 -
os.path.dirname(__file__)
は__file__
から最後のファイル名を除いた部分を取得する。
Python 3.10.6 の場合
執筆時点での最新版。
> python C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
> python get_names.py
C:\Users\(略)\test
C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
> python .\get_names.py
C:\Users\(略)\test
C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
Python 3.10.6 1つ上のディレクトリから実行
同様に比較。
> python C:\Users\(略)\test\get_names.py
C:\Users\(略)
C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
> python test\get_names.py
C:\Users\(略)
C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
> python .\test\get_names.py
C:\Users\(略)
C:\Users\(略)\test\get_names.py
C:\Users\(略)\test
Python 3.10.6 まとめ
-
__file__
が常にフルパスになった。 - ファイルのあるディレクトリで実行するなら、
os.getcwd()
と__file__
は同じ文字列になる。
特に理由が無ければPython3.9以降を使うほうが不具合を避けられるだろう。
パス文字列の結合
パス文字列を結合する際はos.path.join()
を使う。
OS毎の区切り文字の差異(/
or\
)に対応するなど、安全にパス文字列を操作できる。
使用例。
詳細は以前の記事を参照。
import os
from ctypes import cdll
libfile = "rs_add.dll"
cwd = os.getcwd()
libpath = os.path.join(cwd, libfile)
rslib = cdll.LoadLibrary(libpath)
print(rslib.rs_add(4, 6))
おまけ1
VS Codeの「右クリック → ターミナルでPythonファイルを実行する」の方法で実行すると、コマンドの前に&
マークが付いていることに気づいた。
PowerShellの機能らしい。
この方法で実行するとドライブ名が小文字になったり、区切り文字が\
→/
と変化したりする。
手入力で実行する場合でも同様。
一応注意しておく。
> & python c:/Users/(略)/test/get_names.py
C:\Users\(略)\test
c:/Users/(略)/test/get_names.py
c:/Users/(略)/test
おまけ2
os.path.realpath(__file__)
、os.path.abspath(__file__)
なんてのもあるらしい。
参考