为什么在这种情况下os.path.join()无法工作?

414
下面的代码不会连接起来,调试时发现该命令并没有存储整个路径,而只是最后一个条目。
os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/')

当我测试时,它只存储了代码中的/new_sandbox/部分。

16个回答

551

后面那些字符串不应该以斜杠开头。如果它们以斜杠开头,那么它们将被视为“绝对路径”,并且在它们之前的所有内容都将被丢弃。

引用Python文档中os.path.join的说明:

如果组成部分是绝对路径,则所有先前的组成部分都被丢弃,并且从绝对路径组成部分继续连接。

注意在Windows上,与早期的Python版本相比似乎已经更改了驱动器字母的行为:

在Windows上,当遇到绝对路径组件(例如r'\foo')时,驱动器字母不会重置。如果组件包含驱动器字母,则所有先前的组件都被丢弃,驱动器字母被重置。请注意,由于每个驱动器都有当前目录,因此os.path.join("c:", "foo")表示相对于驱动器C上的当前目录的路径(c:foo),而不是c:\foo


108
任何字符串都不应包含“/”字符。os.path.join 的一个重要用途是防止在路径中放置任何斜杠。 - S.Lott
11
str.join() 方法的问题在于它不能消除双斜杠。我认为这是人们使用os.path.join的主要原因。例如,'/'.join(['/etc/', '/conf'])会生成三个斜杠:'/etc///conf'。 - Dustin Rasener
20
你可以使用 os.path.normpath 来实现这个目标。 - Gareth Latty
8
没头绪为什么人们对os.path.join的行为感到沮丧。而在其他语言中,类似的路径拼接库/方法也会表现出完全相同的行为。这更安全,也更合理。 - Don Cheadle
31
这很令人沮丧,因为它是一种隐式的魔法,与“明确优于隐含”的基本原则相反。而且这确实是这样。语言设计者可能认为他们知道得更好,但有时候想要做到这一点存在明显和可证明的安全原因。现在我们做不到了。这就是为什么我们不能拥有好东西的原因。 - Cecil Curry
显示剩余9条评论

170

os.path.join() 的思想是使您的程序跨平台(linux/windows等)。

即使是一个斜杠也会破坏它。

因此,只有在使用某种参考点(如os.environ['HOME']os.path.dirname(__file__))时才有意义。


1
相对路径中的前置斜杠是导致问题的原因...将其删除后问题得到解决..谢谢! - joeyagreco
因为“_即使只有一个斜杠也会破坏它_”而点赞。 - wim

94

os.path.join()可以与os.path.sep结合使用,创建绝对路径而不是相对路径。

os.path.join(os.path.sep, 'home','build','test','sandboxes',todaystr,'new_sandbox')

11
在构建绝对路径的第一个元素中使用os.path.sep比其他任何答案都更好!使用os.path而不是基本字符串方法的整个要点是避免写入斜杠/。将每个子目录作为一个新参数,并删除所有斜杠也很好。最好进行检查以确保 todaystr 不以斜杆开头! ;) - snooze92
5
这也适用于 Windows 系统(Python 2.7.6)。它不会影响 'C:' 目录并会合并子目录。 - rickfoosusa
2
@snooze92,唯一的问题是它更冗长且可读性较差。 - yugr
1
确实。我猜这就是权衡的结果 ;) - snooze92

35

这个答案提供了正确的信息。太棒了。 - Hara

33

为了帮助理解为什么这种令人惊讶的行为并不完全糟糕,请考虑一个接受配置文件名作为参数的应用程序:

config_root = "/etc/myapp.conf/"
file_name = os.path.join(config_root, sys.argv[1])

如果应用程序使用以下方式执行:

$ myapp foo.conf

将使用配置文件/etc/myapp.conf/foo.conf

但是请考虑如果应用程序以以下方式调用会发生什么:

$ myapp /some/path/bar.conf

那么,myapp应该使用位于/some/path/bar.conf的配置文件(而不是类似于/etc/myapp.conf/some/path/bar.conf之类的路径)。

也许这不是最好的方法,但我相信这就是绝对路径行为的动机所在。


2
谢谢!在阅读了您的答案之前,我一直讨厌这种行为!它在https://docs.python.org/3.5/library/os.path.html#os.path.join中有记录,但没有解释其动机。 - Eli_B
2
这一刻,你需要的解决方案恰恰是许多人认为糟糕的。 - ashrasmun
6
我不得不反对,这是完全糟糕的。在这种情况下,你不应该使用天真的 sys.argv 输入来确定是否要在前面添加 config_root。所有的 os.path.join 应该关心的只是连接文件路径元素。 - user5359531
2
问题在于名称选择不当。 os.path.join(p1,p2) 的功能并不是真正地连接p1p2,而是将p2相对于p1(如果p2是绝对路径,则是绝对路径)。 - Nephanth
那么一个特定的用例应该决定函数如何处理所有其他用例吗?这没有意义。 - Gulzar
显示剩余3条评论

16

尝试使用split("/")*的组合处理已存在分隔符的字符串。

import os

home = '/home/build/test/sandboxes/'
todaystr = '042118'
new = '/new_sandbox/'

os.path.join(*home.split("/"), todaystr, *new.split("/"))


工作原理...

split("/")将现有路径转换为列表:['', 'home', 'build', 'test', 'sandboxes', '']

列表前面的*将列表中的每个项目分解出来成为它自己的参数。


2
这将把它转换为相对路径而不是绝对路径。 - user5359531
非常适合处理以斜线开头的端点。谢谢! - Wyrmwood

16

这是因为您的路径'/new_sandbox/'/开头,因此被认为是相对于根目录的路径。请删除前面的/


10
为了使您的函数更具可移植性,请按照以下方式使用它:
os.path.join(os.sep, 'home', 'build', 'test', 'sandboxes', todaystr, 'new_sandbox')

或者

os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')

4

像这样做,不要使用过多的斜杠

root="/home"
os.path.join(root,"build","test","sandboxes",todaystr,"new_sandbox")

在开头使用os.sep代替/ - Kots

4

仅使用new_sandbox尝试

os.path.join('/home/build/test/sandboxes/', todaystr, 'new_sandbox')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接