在python项目中使用相对路径读取文件


78

说我有一个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.pypackage目录运行,则不会出现任何错误。因此,似乎在open(...)其中使用的相对路径仅与从中运行原始文件的位置有关(即__name__ == "__main__")?我不想使用绝对路径。有什么方法可以解决这个问题?


4
作为旁注,引用PEP8:“强烈建议不要将相对进口用于包装内进口。始终对所有导入使用绝对包路径。” 在这里,from package.module import test
频谱

Answers:


114

相对路径是相对于当前工作目录的。如果您不希望自己的道路成为现实,那么它必须是绝对的。

但是,有一个常用的技巧可以从当前脚本构建绝对路径:使用其__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 +现在应该成为标准,所以我首先从jpyams的评论中获取了pathlib版本]


17
当然,如果您使用的是Python 3.4+,则可以使用pathlib.PathPath(__file__).parent.resolve()
jpyams

30

对于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__)) 可解决以下问题:

当我们运行脚本那样: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。由于没有目录“ some_rel_dir_path”,因此FileNotFoundError: [Errno 2] No such file or directory在尝试使用open方法时会给出结果。

通过PyCharm IDE运行/调试配置运行脚本是有效的,因为它运行命令python3 /full/path/to/my_script.py(其中“ / full / path / to”由我们在“运行/调试配置”中的“工作目录”变量中指定),而不是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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.