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

136
假设我有一个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(...)中使用的相对路径只相对于正在运行的文件的位置(即__name__ == "__main__")?如何只使用相对路径来处理这个问题?

8
作为一个附注,引用自PEP8:“强烈不建议在包内导入使用相对路径导入。对于所有导入,请始终使用绝对包路径。” 在这里,from package.module import test - spectras
这应该是一个重复的问题,与如何获取当前正在执行的Python文件的路径和名称?以及(正如我最初关闭的那样)open()给出FileNotFoundError / IOError:'[Errno 2] No such file or directory'相同。问题是一样的,而且非常常见。 - undefined
6个回答

223
相对路径是相对于当前工作目录的。 如果您不希望路径是相对的,它必须是绝对的。
但是有一个常用的技巧可以从当前脚本构建绝对路径:使用其__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版本移动到了第一位]


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

48

适用于 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)]

5

这对我很有效。

with open('data/test.csv') as f:

 

4

我的Python版本是Python 3.5.2,但是被接受的答案提出的解决方案对我无效。我仍然收到错误信息:

FileNotFoundError: [Errno 2] 没有那个文件或目录

当我从终端运行my_script.py时,虽然使用PyCharm(PyCharm 2018.3.2 (社区版))的运行/调试配置可以正常工作。

解决方法:

不要使用:

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相同的原因。因此,尝试使用open方法时会出现FileNotFoundError: [Errno 2] No such file or directory,因为没有名为"some_rel_dir_path"的目录。
从PyCharm IDE Running/Debug Configurations运行脚本可以正常工作,因为它运行的是一个命令python3 /full/path/to/my_script.py(其中"/full/path/to"由我们在Run/Debug Configurations中的"Working directory"变量指定),而不是像从终端运行脚本一样只运行python3 my_script.py

2
这确实是一个警告。显式运行脚本非常不常见,你似乎是第一个注意到的(通常你会在顶部放置#!/usr/bin/env python,将其标记为可执行文件,并作为./myscript.py运行)。但是,pathlib.Path版本并没有这个问题。自Python3.4以来,它可能是更好的选项。 - spectras

1

尝试

with open(f"{os.path.dirname(sys.argv[0])}/data/test.csv", newline='') as f:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接