为什么os.path.join()在这种情况下不起作用?


325

下面的代码将不会加入,调试后,该命令将不会存储整个路径,而只会存储最后一个条目。

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

当我对此进行测试时,它仅存储/new_sandbox/部分代码。

Answers:


426

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

Python文档os.path.join引用

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

请注意,在Windows上,与驱动器号有关的行为与早期的Python版本相比似乎有所变化:

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


85
-1:任何字符串都不应包含“ /”。os.path.join的一个重点是防止在路径中放置任何斜线。
S.Lott

6
当然,str.join()的问题在于它不会消除双斜杠。我认为这是使用os.path.join的人们的主要目的。如'/'.join(['/etc/', '/ CONF'])导致三个斜杠: '/等/// CONF'
达斯汀Rasener

17
@DustinRasener您可以os.path.normpath用来实现该目标。
Gareth Latty

5
不知道人们为什么对os.path.join行为感到沮丧。在其他语言中,等效的路径连接库/方法的行为完全相同。更安全,更有意义。
Don Cheadle 2014年

19
这令人沮丧,因为它是隐式魔术,这与“显式优于隐式” 的基本启发式相反。而且。语言设计师可能会认为他们知道得更多,但是存在明显且明显安全的理由,偶尔需要这样做。现在我们不能。这就是为什么我们不能拥有美好的事物。
塞西尔·库里

151

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

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

因此,仅当与某种参考点(例如os.environ['HOME']或)一起使用时,它才有意义 os.path.dirname(__file__)


75

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

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

8
os.path.sep用作构建绝对路径的第一个元素比这里的任何其他答案都更好!使用os.path而不是基本的str方法的全部目的是避免编写/。将每个子目录作为新参数并删除所有斜杠也很好。确保使用todaystr不以斜杠开头的支票可能是一个好主意!;)
snooze92 2014年

3
这也适用于Windows(python 2.7.6)。它没有干扰'C:\'并加入了子目录。
rickfoosusa


21

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

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类似文件)。

可能不是很好,但是我相信这是绝对路径行为的动机。


谢谢!在阅读您的答案之前,我一直讨厌这种行为!它记录在docs.python.org/3.5/library/os.path.html#os.path.join中,但没有动机。
Eli_B

这一刻,当您确切需要解决方案时,许多人认为这很糟糕。
ashrasmun

12

这是因为您'/new_sandbox/'以a开头,/因此被假定为相对于根目录。拆下龙头/


8

为了使您的功能更具可移植性,请按以下方式使用它:

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

要么

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

8

尝试使用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', '']

* 列表前面的内容将列表中的每个项目分解成自己的参数


3

new_sandbox仅尝试

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

2

这样做,没有太多的斜线

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

0

请注意,如果您使用os.path.join()已经包含点的扩展名,也会遇到类似的问题,当您使用时,扩展名会自动出现os.path.splitext()。在此示例中:

components = os.path.splitext(filename)
prefix = components[0]
extension = components[1]
return os.path.join("avatars", instance.username, prefix, extension)

即使最终您extension可能会得到.jpg一个名为“ foobar”的文件夹,而不是一个名为“ foobar.jpg”的文件。为防止这种情况,您需要单独附加扩展名:

return os.path.join("avatars", instance.username, prefix) + extension

0

你可以strip'/'

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

0

我建议从第二个和后面的字符串中删除字符串os.path.sep,以防止将它们解释为绝对路径:

first_path_str = '/home/build/test/sandboxes/'
original_other_path_to_append_ls = [todaystr, '/new_sandbox/']
other_path_to_append_ls = [
    i_path.strip(os.path.sep) for i_path in original_other_path_to_append_ls
]
output_path = os.path.join(first_path_str, *other_path_to_append_ls)

0
os.path.join("a", *"/b".split(os.sep))
'a/b'

完整版本:

import os

def join (p, f, sep = os.sep):
    f = os.path.normpath(f)
    if p == "":
        return (f);
    else:
        p = os.path.normpath(p)
        return (os.path.join(p, *f.split(os.sep)))

def test (p, f, sep = os.sep):
    print("os.path.join({}, {}) => {}".format(p, f, os.path.join(p, f)))
    print("        join({}, {}) => {}".format(p, f, join(p, f, sep)))

if __name__ == "__main__":
    # /a/b/c for all
    test("\\a\\b", "\\c", "\\") # optionally pass in the sep you are using locally
    test("/a/b", "/c", "/")
    test("/a/b", "c")
    test("/a/b/", "c")
    test("", "/c")
    test("", "c")

如果os.sep实际上是"\"怎么办?然后您的第一个示例变为os.path.join("a", *"/b".split("\\")),它产生"/b"...我怀疑这是预期的结果。
NichtJens

1
更新-我想您必须给一个提示,因为您在本地使用的路径sep与运行的操作系统无关
尼尔·麦吉尔

1
是。另一种选择是,可以在两个常用选项上分开使用……但是其他一些操作系统可以提供第三个。
NichtJens
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.