Python中的相对路径

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

764586 次浏览

参见sys.path 在程序启动时初始化后,该列表的第一项路径[0]是包含用于调用Python解释器的脚本的目录。< / p >

使用此路径作为您应用相对路径 . exe的根文件夹

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

在有脚本的文件中,你想做这样的事情:

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机器上引发一个异常。

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

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

嗨,首先你应该理解函数os.path.abspath(路径)os.path.relpath(路径)

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

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

下面的例子可以让你正确理解上面的概念 < / >强:

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

D: \ \ input1.dic浓缩

D: \ \ input2.dic浓缩

D: \ Copyioconc \ input_file_list.txt

如果你看到上面的文件夹结构,input_file_list.txtCopyofconc文件夹中,而python脚本要处理的文件在浓缩的文件夹中

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

. . \ \ input1.dic浓缩

. . \ \ 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”这行。然后使用命令

input1_path = os.path.abspath (. . \ \ input1.dic浓缩)(将相对路径更改为绝对路径。这里作为当前的工作目录是“D:\Copyofconc”,文件“.\conc\input1.”相对于"D:\Copyofconc")

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

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

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

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

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

另一个适合我的选择是:

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

考虑一下我的代码:

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)

而不是使用

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

这段代码将返回主脚本的绝对路径。

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

这甚至可以在一个模块中工作。

如上所述,接受的答案

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

被接受的答案在某些情况下可能会误导,详情请参阅链接

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

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

现在是2018年,Python很久以前就已经进化到__future__了。那么,如何使用Python 3.4附带的惊人的pathlib来完成任务,而不是苦苦挣扎于osos.pathglob shutil等。

这里有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()

在未来,就这么简单。


此外,我们可以使用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)

最重要命令的摘要

>>> import os
>>> os.path.join('/home/user/tmp', 'subfolder')
'/home/user/tmp/subfolder'
>>> os.path.normpath('/home/user/tmp/../test/..')
'/home/user'
>>> os.path.relpath('/home/user/tmp', '/home/user')
'tmp'
>>> os.path.isabs('/home/user/tmp')
True
>>> os.path.isabs('/tmp')
True
>>> os.path.isabs('tmp')
False
>>> os.path.isabs('./../tmp')
False
>>> os.path.realpath('/home/user/tmp/../test/..') # follows symbolic links
'/home/user'
详细的描述可以在文档中找到。 这些是linux路径。

我认为要与所有系统一起工作,请使用“ntpath”;而不是“os.path”;如今,它在Windows、Linux和Mac OSX上都能很好地工作。

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

根据其他人的建议和pathlib文档,一个简单(但不理想)的解决方案如下(假设我们需要引用的文件是Test/data/users.csv):

# Current file location: Tests/src/long/module/subdir/some_script.py
from pathlib import Path


# back to Tests/
PROJECT_ROOT = Path(__file__).parents[4]
# then down to Test/data/users.csv
CSV_USERS_PATH = PROJECT_ROOT / 'data' / 'users.csv'


with CSV_USERS_PATH.open() as users:
print(users.read())

这是可行的,但看起来有点奇怪,因为如果你移动some_script.py,到项目根的路径可能会改变(因此我们需要改变parents[4]部分)。

我想我找到了一个更好的解决方案,基于同样的想法。 我们将使用一个文件paths.py来存储项目的根目录的位置,与根目录相比,这个文件将保持相同的位置
Tests
├── data
│  └── users.csv
└── src
├── long
│  └── module
│     └── subdir
│        └── some_script.py
├── main.py
└── paths.py

其中paths.py的唯一职责是提供PROJECT_ROOT:

from pathlib import Path


PROJECT_ROOT = Path(__file__).parents[1]

所有脚本现在都可以使用paths.PROJECT_ROOT来表示从项目根开始的绝对路径。例如,在src/long/module/subdir/some_script.py中我们可以有:

from paths import PROJECT_ROOT


CSV_USERS_PATH = PROJECT_ROOT / 'data' / 'users.csv'


def hello():
with CSV_USERS_PATH.open() as f:
print(f.read())

一切都如你所料:

~/Tests/src/$ python main.py


/Users/cglacet/Tests/data/users.csv
hello, user


~/Tests/$ python src/main.py


/Users/cglacet/Tests/data/users.csv
hello, user

main.py脚本简单来说就是:

from long.module.subdir import some_script


some_script.hello()

一个简单的解决办法是

import os
os.chdir(os.path.dirname(__file__))

C:\Users\xyz\myFolderC:\Users\xyz\testdata:

import os
working_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
# C:\Users\xyz\myFolder
print(working_dir)
updated_working_dir = os.path.join(os.path.realpath(working_dir + '/../'), 'testdata')
# C:\Users\xyz\testdata
print(updated_working_dir)

输出

C:\Users\xyz\myFolder
C:\Users\xyz\testdata

例子


下面是一个用Python '3.9.5 '测试的例子:

你的当前目录:'c:\project1\code\'
你想要访问以下文件夹:'c:\project1\dataset\train\'。< br > 然后可以使用以下地址访问该文件夹:'../dataset/train/'

参考文献


如果你想了解更多关于path in Python的信息,请阅读以下内容:

以下是我的总结:

首先,定义名为relpath的工具函数,它将当前文件的相对路径转换为cwd的相对路径

import os
relpath = lambda p: os.path.normpath(os.path.join(os.path.dirname(__file__), p))

然后我们使用它来包装相对于当前文件的路径

path1 = relpath('../src/main.py')

你也可以调用sys.path.append()来导入相对于当前文件位置的文件

sys.path.append(relpath('..')) # so that you can import from upper dir

完整的示例代码:https://gist.github.com/luochen1990/9b1ffa30f5c4a721dab5991e040e3eb1

将当前存档命名为“help”;上面的目录名为"Workshop",模板文件在\Workshop\Templates中,那么在Python中的相对路径是"..\Templates"。

这是将相对路径添加到系统路径集的简单方法。例如,对于目标目录比工作目录高一级(即'/../')的常见情况:

import os
import sys
workingDir = os.getcwd()
targetDir = os.path.join(os.path.relpath(workingDir + '/../'),'target_directory')
sys.path.insert(0,targetDir)

对该解决方案进行了测试:

Python 3.9.6 |封装的conda-forge |(默认,2021年7月11日) 03:37:25) [MSC .1916 64位(AMD64)]