中学英語811710 views
小学算数1200427 views
Computer368106 views
いろは3010814 views
MathPython497343 views
りんご209490 views
小学理科719870 views
教育149510 views
LaTeX962153 views
高校国語788336 views
Help
Tools
NewsSpreadsheetCalendarSlidesTier ListPen ToolIllustrationCrayonPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksWater RippleWater SplashBreaking GlassWood GrainMarble TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsCross SectionsMotion PathMechanicsWavesLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic Theory

English

__file__ が使えないケースと代替手段(frozen, zipimport)

__file__ はスクリプトのパスを取得する便利な変数だが、すべての状況で使えるわけではない。使えないケースと代替手段を理解しておく必要がある。

__file__ が使えないケース

インタラクティブシェル(Python REPL)
exec()eval() で実行されたコード
-c オプションでコマンドラインから実行した場合
PyInstaller などで exe 化した場合(frozen)
zip ファイルからインポートされたモジュール

インタラクティブシェルでの問題

# Python REPL で実行
>>> __file__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '__file__' is not defined

exec() での問題

code = '''
print(__file__)
'''
exec(code)  # NameError: name '__file__' is not defined

PyInstaller で exe 化した場合

PyInstaller でパッケージングすると、__file__ は一時ディレクトリを指すことがある。

# exe 化されたプログラムでは
print(__file__)  # C:\Users\...\AppData\Local\Temp\_MEI12345\script.py

実行ファイルの場所を取得するには sys.executable を使う。

import sys
import os

def get_app_path():
    if getattr(sys, 'frozen', False):
        # PyInstaller で frozen されている
        return os.path.dirname(sys.executable)
    else:
        # 通常の Python スクリプト
        return os.path.dirname(os.path.abspath(__file__))

sys.frozen 属性は PyInstaller によって設定される。

zipimport の場合

Python は zip ファイル内のモジュールをインポートできるが、その場合 __file__ は zip 内のパスを指す。

# module.py が archive.zip 内にある場合
import sys
sys.path.insert(0, 'archive.zip')

import module
print(module.__file__)  # archive.zip/module.py

このパスは通常のファイル操作には使えない。

代替手段

inspect モジュールを使う

inspect モジュールは呼び出し元の情報を取得できる。

import inspect

def get_script_path():
    frame = inspect.currentframe()
    if frame is None:
        return None
    
    # 呼び出し元のフレームを取得
    caller_frame = frame.f_back
    if caller_frame is None:
        return None
    
    return caller_frame.f_code.co_filename

sys.argv[0] を使う

コマンドラインから実行された場合、sys.argv[0] にスクリプトのパスが入る。

import sys
import os

script_path = os.path.abspath(sys.argv[0])
script_dir = os.path.dirname(script_path)

ただし、python -m module で実行した場合や、インポートされた場合は期待通りに動かない。

importlib.resources を使う

パッケージ内のリソースファイルにアクセスする場合は importlib.resources が最も信頼性が高い。

# Python 3.9 以降
from importlib.resources import files

# パッケージ内のファイルを取得
config_text = files('mypackage').joinpath('config.json').read_text()

# パスが必要な場合
from importlib.resources import as_file

with as_file(files('mypackage').joinpath('data.bin')) as path:
    # path は一時ファイルのパス(zip 内でも動作)
    with open(path, 'rb') as f:
        data = f.read()

堅牢な実装例

様々な状況に対応する関数の例を示す。

import sys
import os

def get_base_path():
    """スクリプトまたは実行ファイルのディレクトリを取得"""
    
    # PyInstaller で frozen されている場合
    if getattr(sys, 'frozen', False):
        return os.path.dirname(sys.executable)
    
    # __file__ が利用可能な場合
    if '__file__' in globals():
        return os.path.dirname(os.path.abspath(__file__))
    
    # sys.argv[0] にフォールバック
    if sys.argv[0]:
        return os.path.dirname(os.path.abspath(sys.argv[0]))
    
    # 最終手段:カレントディレクトリ
    return os.getcwd()