Чтение файла с использованием относительного пути в проекте 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'
Однако, если я запускаю module.py
из каталога package
, я не получаю ошибок. Таким образом, кажется, что относительный путь, используемый в open(...)
, относится только к тому, где выполняется исходный файл (i.e __name__ == "__main__"
)? Я не хочу использовать абсолютные пути. Каковы некоторые способы борьбы с этим?
Ответы
Ответ 1
Относительные пути относятся к текущему рабочему каталогу.
Если вы не хотите, чтобы ваш путь был, он должен быть абсолютным.
Но есть часто используемый прием для построения абсолютного пути из текущего скрипта: используйте его специальный атрибут __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
в этом примере. Не то чтобы это строго необходимо, но это позволяет избежать неожиданностей, если вы в какой-то момент измените текущий рабочий каталог и ваш модуль был импортирован с использованием относительного пути.
Ответ 2
Для 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
из терминала. Хотя он работал нормально, когда я запускал его через Run/Debug Configurations из 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__))
решает следующую проблему:
Когда мы запускаем наш скрипт так: python3 my_script.py
переменная __file__
имеет только строковое значение my_script.py без пути, ведущего к этому конкретному сценарию. Вот почему метод dirname(__file__)
возвращает пустую строку "". Это также резон, почему my_path = os.path.abspath(os.path.dirname(__file__)) + some_rel_dir_path
на самом деле то же самое, что и my_path = some_rel_dir_path
. Следовательно, FileNotFoundError: [Errno 2] No such file or directory
дается при попытке использовать метод open
, потому что нет такого каталога, как "some_rel_dir_path".
Запуск сценария из PyCharm IDE Запуск/отладка конфигураций сработал, потому что он запускает команду python3 /full/path/to/my_script.py
(где "/full/path/to" указан нами в переменной "Рабочий каталог" в конфигурациях Run/Debug) вместо просто python3 my_script.py
как это делается, когда мы запускаем его из терминала.
Надеюсь, что это будет полезно.