Python中的相对路径

428 投票
21 回答
895829 浏览
提问于 2025-04-15 11:52

我正在为工作写一个简单的辅助脚本,这个脚本会把我们代码库中的几个模板文件复制到当前的目录里。不过,我并不知道模板文件存放的绝对路径。虽然我有一个相对路径,但当我运行这个脚本时,它会把这个路径当作是相对于当前工作目录的路径。有没有办法让这个相对路径是从脚本所在的位置开始的呢?

21 个回答

76

你需要用到 os.path.realpath 这个功能(下面的例子是把父目录添加到你的路径中)

import sys,os
sys.path.append(os.path.realpath('..'))
116

现在是2018年,Python早就发展到了__future__这个阶段。所以我们可以用Python 3.4自带的超棒的pathlib来完成这个任务,而不是费力地去用osos.pathglobshutil等等。

这里我们有三个路径(可能有重复的):

  • mod_path:这是简单辅助脚本的路径。
  • src_path:这里面有几个等待复制的模板文件
  • cwd当前目录,就是那些模板文件的目的地。

问题是:我们并没有src_path的完整路径,只知道它相对于mod_path相对路径

现在让我们用这个超棒的pathlib来解决这个问题:

# Hope you don't be imprisoned by legacy Python code :)
from pathlib import Path

# `cwd`: current directory is straightforward
cwd = Path.cwd()

# `mod_path`: According to the accepted answer and combine with future power
# if we are in the `helper_script.py`
mod_path = Path(__file__).parent
# OR if we are `import helper_script`
mod_path = Path(helper_script.__file__).parent

# `src_path`: with the future power, it's just so straightforward
relative_path_1 = 'same/parent/with/helper/script/'
relative_path_2 = '../../or/any/level/up/'
src_path_1 = (mod_path / relative_path_1).resolve()
src_path_2 = (mod_path / relative_path_2).resolve()

将来,这样做就是这么简单。


而且,我们可以用pathlib来选择、检查、复制或移动那些模板文件:

if src_path != cwd:
    # When we have different types of files in the `src_path`
    for template_path in src_path.glob('*.ini'):
        fname = template_path.name
        target = cwd / fname
        if not target.exists():
            # This is the COPY action
            with target.open(mode='wb') as fd:
                fd.write(template_path.read_bytes())
            # If we want MOVE action, we could use:
            # template_path.replace(target)
546

在包含脚本的文件中,你想要做的事情大概是这样的:

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

这样做可以让你找到你想要的文件的绝对路径。需要注意的是,如果你在使用setuptools,最好使用它的包资源API

更新:我在这里回复一个评论,以便可以粘贴代码示例。:-)

我是不是可以理解为__file__并不总是可用的(比如直接运行文件而不是导入它时)?

我假设你提到的直接运行文件是指__main__脚本。如果是这样的话,在我的系统上(OS X 10.5.7上的python 2.5.1)似乎并不是这样:

#foo.py
import os
print os.getcwd()
print __file__

#in the interactive interpreter
>>> import foo
/Users/jason
foo.py

#and finally, at the shell:
~ % python foo.py
/Users/jason
foo.py

不过,我知道在C扩展中__file__有一些特殊情况。例如,我可以在我的Mac上这样做:

>>> import collections #note that collections is a C extension in Python 2.5
>>> collections.__file__
'/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-
dynload/collections.so'

但是在我的Windows机器上这样做会引发一个异常。

撰写回答