web-dev-qa-db-ja.com

pythonプロジェクトの相対パスを使用したファイルの読み取り

次のような構造のpythonプロジェクトがあるとします。

project
    /data
        test.csv
    /package
        __init__.py
        module.py
    main.py

__init__.py

from .module import test

module.py

import csv

with open("..data/test.csv") as f:
    test = [line for line in csv.reader(f)]

main.py

import package

print(package.test)

main.pyを実行すると、次のエラーが表示されます。

 C:\Users\Patrick\Desktop\project>python main.py
Traceback (most recent call last):
  File "main.py", line 1, in <module>
    import package
  File "C:\Users\Patrick\Desktop\project\package\__init__.py", line 1, in <module>
    from .module import test
  File "C:\Users\Patrick\Desktop\project\package\module.py", line 3, in <module>
    with open("../data/test.csv") as f:
FileNotFoundError: [Errno 2] No such file or directory: '../data/test.csv'

ただし、packageディレクトリからmodule.pyを実行してもエラーは発生しません。したがって、open(...)で使用される相対パスは、元のファイルが実行されている場所(つまり__== "__main__")にのみ関連しているようです。絶対パスを使用したくありません。これに対処する方法は何ですか?

38
pbreach

相対パスは 現在の作業ディレクトリ に相対的です。あなたがあなたの道になりたくないなら、それは絶対でなければなりません。

しかし、現在のスクリプトから絶対パスを作成するためによく使用されるトリックがあります: __file__ 特別な属性を使用します:

import csv
import os.path

my_path = os.path.abspath(os.path.dirname(__file__))
path = os.path.join(my_path, "../data/test.csv")
with open(path) as f:
    test = list(csv.reader(f))

python 3.4から、インポートされたモジュールでは__file__は常に絶対であり、この例ではos.path.abspath部分をドロップできます。厳密に必要というわけではありませんが、現在の作業ディレクトリをある時点で変更し、モジュールが相対パスを使用してインポートされた場合に驚くことはありません。

57
spectras

Python 3.4+の場合:

import csv
from pathlib import Path

base_path = Path(__file__).parent
file_path = (base_path / "../data/test.csv").resolve()

with open(file_path) as f:
    test = [line for line in csv.reader(f)]
8
aksh1618