Читання файлу за допомогою відносного шляху в проекті python


83

Скажімо, у мене є проект на 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'

Однак, якщо я запускаю module.pyз packageкаталогу, я не отримую помилок. Отже, здається, що відносний шлях, який використовується в, open(...)є лише відносно того, звідки запускається файл-джерело (тобто __name__ == "__main__")? Я не хочу використовувати абсолютні шляхи. Які є способи боротьби з цим?


4
Наостанок, цитуючи PEP8: «Відносний імпорт для імпорту всередині упаковки вкрай не рекомендується. Завжди використовуйте абсолютний шлях до пакунку для всього імпорту. " Тут from package.module import test.
спектри

Відповіді:


120

Відносні шляхи відносяться до поточного робочого каталогу . Якщо ви не хочете, щоб ваш шлях був, він повинен бути абсолютним.

Але є часто використовуваний фокус для побудови абсолютного шляху з поточного сценарію: використовуйте його __file__спеціальний атрибут:

from pathlib import Path

path = Path(__file__).parent / "../data/test.csv"
with path.open() as f:
    test = list(csv.reader(f))

Для цього потрібен python 3.4+ (для модуля pathlib ).

Якщо вам все ще потрібна підтримка старих версій, ви можете отримати той самий результат за допомогою:

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))

[ Редагувати 2020: python3.4 + тепер повинен стати нормою, тому я переніс версію pathlib, натхненну спочатку коментарем jpyams]


18
Звичайно, якщо ви використовуєте Python 3.4+, ви можете просто використати pathlib.Path :Path(__file__).parent.resolve()
jpyams

35

Для 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)]

3

Моя версія Python - Python 3.5.2, і рішення, запропоноване у прийнятій відповіді, для мене не спрацювало. Мені все-таки дали помилку

FileNotFoundError: [Errno 2] No such file or directory

коли я бігав my_script.pyз терміналу. Хоча він працював нормально, коли я запускаю його через Запуск / Налагодження конфігурацій з PyCharm IDE (PyCharm 2018.3.2 (Community Edition)).

Рішення :

замість використання:

my_path = os.path.abspath(os.path.dirname(__file__)) + some_rel_dir_path 

як пропонується у прийнятій відповіді, я використав:

my_path = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) + some_rel_dir_path

Пояснення : Перехід os.path.dirname(__file__)на os.path.dirname(os.path.abspath(__file__)) вирішує таку проблему:

Коли ми запускаємо наш скрипт так: змінні має тільки значення рядка «my_script.py» без шляху , що ведуть до певним сценарієм. Ось чому метод повертає порожній рядок "". Це також резонанс, чому насправді це те саме, що . Отже , дається при спробі використання методу, оскільки не існує каталогу типу "some_rel_dir_path".python3 my_script.py__file__dirname(__file__)my_path = os.path.abspath(os.path.dirname(__file__)) + some_rel_dir_pathmy_path = some_rel_dir_pathFileNotFoundError: [Errno 2] No such file or directoryopen

Запуск сценарію з PyCharm IDE Running / Debug Configurations спрацював, оскільки він запускає команду python3 /full/path/to/my_script.py(де "/ full / path / to" вказано нами у змінній "Working directory" у Run / Debug Configurations), а не так, python3 my_script.pyяк це робиться, коли ми запустити його з терміналу.

Сподіваюся, це буде корисно.


2
Це справді застереження. Це настільки рідко, коли явно запускається сценарій, що, здається, ви помічаєте першим (зазвичай ви ставите #!/usr/bin/env pythonвгорі, позначаєте його виконуваним та запускаєте як ./myscript.py). У pathlib.Pathверсії немає цієї проблеми, хоча це, мабуть, кращий варіант, оскільки python3.4.
спектри



0

Мене загриміло, коли запрацював наступний код.

import os

for file in os.listdir("../FutureBookList"):
    if file.endswith(".adoc"):
        filename, file_extension = os.path.splitext(file)
        print(filename)
        print(file_extension)
        continue
    else:
        continue

Отже, я перевірив документацію і там написано:

Змінено у версії 3.6: Приймає подібний до шляху об’єкт.

об'єкт, подібний до шляху :

Об'єкт, що представляє шлях до файлової системи. Шлях-подібний об'єкт - це або str, або ...

Я трохи більше копав, і також працює наступне:

with open("../FutureBookList/file.txt") as file:
   data = file.read()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.