Python中的相对路径

bau*_*ack 204 python path relative-path

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

Jas*_*ker 273

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

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')
Run Code Online (Sandbox Code Playgroud)

这将为您提供所需文件的绝对路径.请注意,如果您使用的是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
Run Code Online (Sandbox Code Playgroud)

但是,我知道__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'
Run Code Online (Sandbox Code Playgroud)

但是,这会在我的Windows机器上引发异常.

  • 注意你应该在任何地方使用os.path.join来实现可移植性:`filename = os.path.join(dir,'relative','path','to','file','you','want')` (21认同)
  • `os.path.dirname(__ file __)`可以给出一个空字符串,使用`os.path.dirname(os.path.abspath(__ file __))` (16认同)
  • 这是一个小问题,但请不要使用dir作为变量名,因为它是内置的. (11认同)
  • 我是否正确地认为 __file__ 并不总是可用(例如,当您直接运行文件而不是导入文件时)? (2认同)

use*_*762 58

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

import sys,os
sys.path.append(os.path.realpath('..'))
Run Code Online (Sandbox Code Playgroud)

  • `os.path.realpath('..')`为你提供**当前工作目录**的父目录.这通常不是你想要的**. (7认同)
  • 这似乎为脚本的运行目录提供了父级,而不是脚本的位置。 (3认同)
  • `os.path.dirname(__ file __)`给了我一个空字符串.这非常有效. (2认同)

Ahm*_*med 50

如接受的答案中所述

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

我只想补充一点

后一个字符串不能以反斜杠开头,事实上没有字符串应该包含反斜杠

应该是这样的

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'relative','path','to','file','you','want')
Run Code Online (Sandbox Code Playgroud)

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

  • 是的,使用`os.path.join`更好,因为它将它们与特定于操作系统的分隔符连接起来. (4认同)
  • 这个答案现在已经过时了,因为顶部的答案已经被编辑为在 `os.path.join()` 中使用正确的相对路径。剩下的是优先为每个路径元素使用单独的字符串而不是硬编码路径分隔符。 (2认同)

小智 13

考虑我的代码:

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)
Run Code Online (Sandbox Code Playgroud)


Tom*_*eys 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'
Run Code Online (Sandbox Code Playgroud)

  • 这不一定是真的.通常sys.path [0]是一个空字符串或一个点,它是当前目录的相对路径.如果需要当前目录,请使用os.getcwd. (3认同)

YaO*_*OzI 11

现在是2018年,Python已经发展到__future__很久以前了.因此,如何使用神奇pathlib与Python 3.4来完成任务,而不是疲于应付os,os.path,glob,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()
Run Code Online (Sandbox Code Playgroud)

在未来,就这么简单.: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)
Run Code Online (Sandbox Code Playgroud)

  • 总结: `from pathlib import Path` `script_dir=Path(__file__).parent` `template_path=(script_dir / template_name).resolve()` (4认同)

cgl*_*cet 8

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

\n
# Current file location: Tests/src/long/module/subdir/some_script.py\nfrom pathlib import Path\n\n# back to Tests/\nPROJECT_ROOT = Path(__file__).parents[4]\n# then down to Test/data/users.csv\nCSV_USERS_PATH = PROJECT_ROOT / \'data\' / \'users.csv\'  \n\nwith CSV_USERS_PATH.open() as users:\n    print(users.read())\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

我想我找到了一个更好的解决方案,基于同样的想法。\n我们将使用一个文件paths.py来存储项目的根目录所在的位置,与根目录相比,该文件将保留在相同的位置。

\n
Tests\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 data\n\xe2\x94\x82  \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 users.csv\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 src\n   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 long\n   \xe2\x94\x82  \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 module\n   \xe2\x94\x82     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 subdir\n   \xe2\x94\x82        \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 some_script.py\n   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main.py\n   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 paths.py\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
from pathlib import Path\n\nPROJECT_ROOT = Path(__file__).parents[1]\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
from paths import PROJECT_ROOT\n\nCSV_USERS_PATH = PROJECT_ROOT / \'data\' / \'users.csv\'\n\ndef hello():\n    with CSV_USERS_PATH.open() as f:\n        print(f.read())\n
Run Code Online (Sandbox Code Playgroud)\n

一切都按预期进行:

\n
~/Tests/src/$ python main.py\n\n/Users/cglacet/Tests/data/users.csv\nhello, user\n\n~/Tests/$ python src/main.py\n\n/Users/cglacet/Tests/data/users.csv\nhello, user\n
Run Code Online (Sandbox Code Playgroud)\n

脚本main.py很简单:

\n
from long.module.subdir import some_script\n\nsome_script.hello()\n
Run Code Online (Sandbox Code Playgroud)\n


Mar*_*hke 6

最重要命令的摘要

>>> 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'
Run Code Online (Sandbox Code Playgroud)

详细的描述可以在文档中找到。这些是 Linux 路径。Windows 的工作方式应该类似。


smi*_*ley 5

而不是使用

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')
Run Code Online (Sandbox Code Playgroud)

如在接受的答案中,使用它会更健壮:

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')
Run Code Online (Sandbox Code Playgroud)

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

这些答案提供了更多细节:https://stackoverflow.com/a/31867043/5542253/sf/answers/3535171/

  • `inspect.stack()`是一个*昂贵的*函数来调用.它检索所有堆栈帧的信息,然后丢弃,然后只获取最重要的堆栈帧.它基本上在模块对象上调用`inspect.getfile()`,它只返回`module .__ file__`.你使用`__file__`要好得多. (5认同)

归档时间:

查看次数:

339291 次

最近记录:

7 年,5 月 前