Python中的相对路径


244

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


Answers:


324

在具有脚本的文件中,您想要执行以下操作:

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

但是,我确实知道__file__在C扩展上有一些怪癖。例如,我可以在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计算机上引发了异常。


1
我认为文件并非总是可用(例如,当您直接运行文件而不是导入文件时)是否正确?
斯蒂芬·埃德蒙兹

@Stephen Edmonds我使用的是我运行的文件,而不是导入的文件,它工作得很好。
baudtack

22
请注意,您应该在各处使用os.path.join来实现可移植性:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
福特

22
os.path.dirname(__file__)可以给一个空字符串,os.path.dirname(os.path.abspath(__file__))改用
Dmitry Trofimov,2015年

14
这是一件小事,但是请不要使用dir作为变量名,因为它是内置变量。
戴维(David)

63

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

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

2
os.path.dirname(__file__)给我一个空字符串。这工作得很好。
Darragh Enright 2014年

3
这似乎为脚本的运行目录提供了父级,而不是脚本所在的位置。
Coquelicot

10
os.path.realpath('..')给您当前工作目录的父目录。通常这不是您想要的。
马丁·彼得斯

1
@DarraghEnright:这仅在Python脚本到exe打包环境中发生。那是很少的例外之一,其中依赖当前的工作目录是替代方法。
马丁·彼得斯

52

如已接受的答案中所述

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

我只想补充一点

后面的字符串不能以反斜杠开头,实际上任何字符串都不应包含反斜杠

应该是这样的

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

在某些情况下,可接受的答案可能会误导您,请参阅链接以获取详细信息


4
是的,使用os.path.join更好,因为它将它们与特定于操作系统的分隔符结合在一起。
Farshid T

'/relative/path...'不是相对路径。那是故意的吗?
steveire

该答案现已过时,因为已对最高答案进行了编辑以使用中的正确相对路径os.path.join()。剩下的是在对路径分隔符进行硬编码的情况下,优先为每个路径元素使用单独的字符串。
马丁·彼得斯

@MartijnPieters是的,已经编辑了最上面的答案,以部分匹配此内容,但是单独的字符串不是首选项-像这样分隔字符串使它们独立于操作系统。
jshrimp29 '19

26

现在是2018年,Python已经发展到__future__很久以前了。因此,如何使用神奇pathlib与Python 3.4来完成任务,而不是疲于应付osos.pathglobshutil,等。

因此,这里有3条路径(可能是重复的):

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

将来,它就这么简单。:D


此外,我们可以选择并检查和复制/移动这些模板文件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)

14

考虑我的代码:

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)

当我在Windows中运行此文件时,收到错误消息:FileNotFoundError:[错误2]没有这样的文件或目录:'<path>',其中<path>具有正确的路径段,但使用\\作为分隔符。
lonstar

11

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

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

>>> 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'

3
不一定是真的。通常sys.path [0]是一个空字符串或一个点,它是当前目录的相对路径。如果需要当前目录,请使用os.getcwd。
杰森·贝克

原始张贴者评论说,当前工作目录是建立相对路径的错误位置。您正确地说sys.path [0]并非始终有效。
汤姆·莱斯

不,sys.path[0]并非总是设置为父目录。Python代码可以与被调用-c-m或经由嵌入式解释,在该点sys.path[0]被设置为不同的东西完全。
马丁·彼得斯

6

而不是使用

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

如公认的答案中所示,使用起来会更可靠:

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

因为使用__file__将返回从中加载模块的文件(如果是从文件中加载的),那么如果从其他位置调用了带有脚本的文件,则返回的目录将不正确。

这些答案提供了更多详细信息:https : //stackoverflow.com/a/31867043/5542253https://stackoverflow.com/a/50502/5542253


5
inspect.stack()是一个昂贵的函数。它检索所有堆栈帧的信息,然后将其丢弃,仅获得最上面的帧。它基本上调用inspect.getfile()模块对象,该对象仅返回module.__file__。只使用会更好__file__
马亭皮特斯

4

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

总之os.path.abspath则(路径)使一个相对路径绝对路径。如果提供的路径本身是绝对路径,则该函数将返回相同的路径。

类似地 os.path.relpath(path)绝对路径设为相对路径。如果提供的路径本身是相对路径,则该函数将返回相同的路径。

下面的示例可以使您正确理解以上概念

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

D:\ conc \ input1.dic

D:\ conc \ input2.dic

D:\ Copyioconc \ input_file_list.txt

如果您看到上述文件夹结构,则copyofconc文件夹中存在input_file_list.txt,而python脚本中要处理的文件则存在于 conc文件夹文件

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

.. \ conc \ input1.dic

.. \ conc \ input2.dic

我的python脚本存在于D中:驱动器中。

并且input_file_list.txt文件中提供的相对路径相对于input_file_list.txt文件的路径。

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

因为我的相对路径是相对于input_file_list.txt的,即“ D:\ Copyofconc”,所以我必须将当前工作目录更改为 “ D:\ Copyofconc”

因此,我必须使用os.chdir('D:\ Copyofconc'),因此当前工作目录应为“ D:\ Copyofconc”

现在获取文件input1.dicinput2.dic,我将读取“ .. \ conc \ input1.dic”行,然后应使用以下命令

input1_path = os.path.abspath('.. \ conc \ input1.dic')(将相对路径更改为绝对路径。此处,由于当前工作目录为“ D:\ Copyofconc”,因此文件为“。\ conc \ input1”。 dic”应相对于“ D:\ Copyofconc”进行访问)

所以input1_path应该是“ D:\ conc \ input1.dic”


4

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

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

即使在模块中也可以使用。


您可以使用而不是重新导入sys.modules['__main__'].__file__
马丁·彼得斯

3

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

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

0

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

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

1
这取决于当前的工作目录,该目录可能与您实际需要的目录完全不同。
马丁·彼得斯

-2

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

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

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

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


是的,所以它将在CWD上正常工作,而这并不是OP所要求的。想要从脚本位置工作。
Samy Bencherif
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.