python中的相对路径

Relative paths in Python

我正在为工作构建一个简单的助手脚本,它将把代码库中的几个模板文件复制到当前目录中。但是,我没有到存储模板的目录的绝对路径。我确实有一个来自脚本的相对路径,但是当我调用脚本时,它将其视为相对于当前工作目录的路径。是否有方法指定此相对URL来自脚本的位置?


在包含脚本的文件中,您希望执行以下操作:

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

这将为您提供要查找的文件的绝对路径。请注意,如果您使用的是安装工具,那么您可能应该改为使用其包资源API。

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

Am I correct in thinking that __file__ is not always available (e.g. when you run the file directly rather than importing it)?

当您提到直接运行文件时,我假设您是指__main__脚本。如果是这样,那么在我的系统上就不会出现这种情况(在OS X 10.5.7上是python 2.5.1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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

不过,我知道__file__在C扩展上有一些奇怪之处。例如,我可以在Mac上执行此操作:

1
2
3
4
>>> 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计算机上引发了一个异常。


您需要os.path.realpath(下面的示例将父目录添加到您的路径中)

1
2
import sys,os
sys.path.append(os.path.realpath('..'))


如接受答案中所述

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

我只是想补充一下

the latter string can't begin with the backslash , infact no string
should include a backslash

应该是这样的

1
2
3
import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'relative','path','to','file','you','want')

在某些情况下,接受的答案可能会产生误导,有关详细信息,请参阅此链接。


考虑我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import os


def readFile(filename):
    filehandle = open(filename)
    print filehandle.read()
    filehandle.close()



fileDir = os.path.dirname(os.path.realpath('__file__'))
print fileDir

#For accessing the file in the same folder
filename ="same.txt"
readFile(filename)

#For accessing the file in a folder contained in the current folder
filename = os.path.join(fileDir, 'Folder1.1/same.txt')
readFile(filename)

#For accessing the file in the parent folder of the current folder
filename = os.path.join(fileDir, '../same.txt')
readFile(filename)

#For accessing the file inside a sibling folder.
filename = os.path.join(fileDir, '../Folder2/same.txt')
filename = os.path.abspath(os.path.realpath(filename))
print filename
readFile(filename)

参见SysPATH在程序启动时初始化时,此列表的第一项path[0]是包含用于调用Python解释器的脚本的目录。

将此路径用作应用相对路径的根文件夹

1
2
3
4
5
6
7
>>> import sys
>>> import os.path
>>> sys.path[0]
'C:\\Python25\\Lib\\idlelib'
>>> os.path.relpath(sys.path[0],"path_to_libs") # if you have python 2.6
>>> os.path.join(sys.path[0],"path_to_libs")
'C:\\Python25\\Lib\\idlelib\\path_to_libs'


现在是2018年了,而python早就进化到了__future__。那么,使用与Python3.4一起提供的令人惊叹的pathlib来完成任务,而不是与osos.pathglobshutil等作斗争,怎么样?

所以我们这里有三条路径(可能是重复的):

  • mod_path:这是简单助手脚本的路径
  • src_path:包含两个等待复制的模板文件。
  • cwd:当前目录,这些模板文件的目的地。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 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()

在未来,就这么简单。D

此外,我们还可以使用pathlib选择、检查和复制/移动这些模板文件:

1
2
3
4
5
6
7
8
9
10
11
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)


而不是使用

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

与公认的答案一样,使用以下内容更为可靠:

1
2
3
4
import inspect
import os
dirname = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

因为使用文件将返回从中加载模块的文件,如果从文件加载模块,那么如果从其他地方调用带有脚本的文件,则返回的目录将不正确。

这些答案更详细:https://stackoverflow.com/a/31867043/5542253和https://stackoverflow.com/a/50502/5542253


此代码将返回到主脚本的绝对路径。

1
2
3
import os
def whereAmI():
    return os.path.dirname(os.path.realpath(__import__("__main__").__file__))

即使在模块中也可以。


首先,您应该了解函数os.path.abspath(path)和os.path.relpath(path)

简言之,os.path.abspath(path)使相对路径指向绝对路径。如果提供的路径本身是绝对路径,那么函数返回相同的路径。

类似地,os.path.relpath(path)使相对路径成为绝对路径。如果提供的路径本身是相对路径,那么函数返回相同的路径。

下面的示例可以让您正确理解上述概念:

假设我有一个文件input_file_list.txt,其中包含要由python脚本处理的输入文件列表。

D:concinput1.dic

D:concinput2.dic

D:copyiconcinput_file_list.txt文件

如果您看到上面的文件夹结构,那么在copyofcconc文件夹中存在input_file_list.txt,python脚本要处理的文件在conc文件夹中存在。

但文件input_file_list.txt的内容如下:

..conc输入1.dic

..conc输入2.dic

我的python脚本出现在d:drive中。

在input_file_list.txt文件中提供的相对路径与input_file_list.txt文件的路径相对。

所以当python脚本执行当前工作目录时(使用os.getcwd()获取路径)

由于我的相对路径是相对于输入文件"d:copyofcont"的,所以我必须将当前工作目录更改为"d:copyofcont"。

所以我必须使用os.chdir("d:copyofcconc"),所以当前的工作目录应该是"d:copyofcconc"。

现在要获取input1.dic和input2.dic文件,我将读取"..concinput1.dic"行,然后使用命令

input1_path=os.path.abspath('..concinput1.dic')(将相对路径更改为绝对路径。由于当前工作目录为"d:copyofcconc",因此文件".concinput1.dic"应相对于"d:copyofcconc"进行访问。

所以input1_路径应该是"d:concinput1.dic"


一个对我有用的替代方案:

1
2
this_dir = os.path.dirname(__file__)
filename = os.path.realpath("{0}/relative/file.path".format(this_dir))

对我有用的是使用sys.path.insert。然后我指定了我需要去的目录。例如,我只需要上一个目录。

1
2
import sys
sys.path.insert(0, '../')


我不确定这是否适用于某些旧版本,但我相信Python3.3具有本机相对路径支持。

例如,以下代码应在与python脚本相同的文件夹中创建文本文件:

1
open("text_file_name.txt","w+t")

(注意,如果是相对路径,则开头不应该有正斜杠或反斜杠)