如何在Python中获取父目录?

541

有人能告诉我如何以跨平台的方式在 Python 中获取路径的父目录吗?例如:

C:\Program Files ---> C:\

C:\ ---> C:\

如果目录没有父级目录,则返回该目录本身。这个问题看起来很简单,但我通过谷歌也找不到答案。


21个回答

769

Python 3.4

使用 pathlib 模块。

from pathlib import Path
path = Path("/here/your/path/file.txt")
print(path.parent.absolute())

旧回答

试试这个:

import os
print os.path.abspath(os.path.join(yourpath, os.pardir))

其中yourpath是您要获取其父级路径的路径。


172
您的答案是正确的,但表述有些复杂;os.path.dirname 是此功能的函数,就像 a+=5-4a+=1 更加复杂一样。问题仅要求父目录,而不涉及其是否存在或假设符号链接妨碍了真正的父目录。 - tzot
2
我试图将代码片段更改为 os.pardir,但不幸的是 Stack Overflow 提示“编辑必须至少包含 6 个非空格字符”。它似乎足够聪明,可以检测到空格填充;也许 OP 以后可以纠正这个问题... - monsur
56
很遗憾,根据路径是否包含尾随斜杠,os.path.dirname会给出不同的结果。如果你想得到可靠的结果,就需要使用上面答案中提到的os.path.join方法。 - Artfunkel
4
谢谢。出于完备性考虑,您的评论应该附在答案中。 - tzot
27
由于这个问题显然足够复杂,需要在StackOverflow上提问,因此我认为应该将其作为内置函数添加到os.path库中。 - antred
显示剩余8条评论

398

使用os.path.dirname

>>> os.path.dirname(r'C:\Program Files')
'C:\\'
>>> os.path.dirname('C:\\')
'C:\\'
>>>

注意:根据路径中是否包含结尾斜杠,os.path.dirname()会返回不同的结果。这可能与您想要的语义相符,也可能不相符。请参考 @kender 的答案,使用 os.path.join(yourpath, os.pardir)


8
os.path.dirname(r'C:\Program Files') 是什么?Python只是给出了文件'Program Files'所在的目录。此外,它甚至不必存在,看这个例子:os.path.dirname(r'c:\i\like\to\eat\pie') 的输出结果是 'c:\\i\\like\\to\\eat' - Nick T
52
原帖并未说明目录必须存在。有很多路径名方法只涉及字符串操作。要验证路径名是否实际存在需要访问磁盘。这取决于应用程序,可能是需要的,也可能是不需要的。 - Wai Yip Tung
12
此解决方案对尾随的 os.sep 敏感。例如,当 os.sep=='/' 时,dirname(foo/bar) -> foo,但dirname(foo/bar/) -> foo/bar。请注意,在处理带有尾部分隔符的路径时,此行为可能会影响预期结果。 - marcin
9
这是有意为之的。这取决于带有斜杠/的路径的解释。您是否认为"path1"等同于"path1/"?该库使用最一般的解释方式,即将它们视为不同。在某些情况下,人们可能希望将它们视为等价的。在这种情况下,您可以先执行rstrip('/')。如果该库选择了另一种解释方式,则会失去准确性。 - Wai Yip Tung
4
@Ryan,我对此并不了解。整个RFC 1808都是为解决URI中相对路径的问题而编写的,并且讨论了尾部/的存在和缺失所涉及的微妙问题。如果你知道任何说明它们在一般情况下应被视为等同的文档,请指出来。 - Wai Yip Tung
显示剩余4条评论

163

Pathlib方法(Python 3.4+)

from pathlib import Path
Path('C:\Program Files').parent
# Returns a Pathlib object

传统方法

import os.path
os.path.dirname('C:\Program Files')
# Returns a string

我该使用哪种方法?

如果以下情况符合,请使用传统方法:

  • 您担心现有代码在使用Pathlib对象时会生成错误。(因为Pathlib对象无法与字符串连接。)

  • 您的Python版本低于3.4。

  • 您需要一个字符串,并且已经得到了一个字符串。例如,如果您有一个表示文件路径的字符串,并且您想获取其父目录以便将其放入JSON字符串中。对于这个问题,将其转换为Pathlib对象再转换回来可能有些愚蠢。

如果以上情况都不适用,则使用Pathlib。


什么是Pathlib?

如果您不知道Pathlib是什么,那么Pathlib模块是一个非常好的模块,可以让您更轻松地处理文件。大多数如果不是所有内置的Python模块都可以接受Pathlib对象和字符串。下面我从Pathlib文档中摘选了一些示例,展示了一些Pathlib的妙处。

浏览目录树:

>>> p = Path('/etc')
>>> q = p / 'init.d' / 'reboot'
>>> q
PosixPath('/etc/init.d/reboot')
>>> <a rel="noreferrer" href="https://docs.python.org/3/library/pathlib.html#pathlib.Path.resolve">q.resolve()</a>
PosixPath('/etc/rc.d/init.d/halt')

查询路径属性:

>>> <a rel="noreferrer" href="https://docs.python.org/3/library/pathlib.html#pathlib.Path.exists">q.exists()</a>
True
>>> <a rel="noreferrer" href="https://docs.python.org/3/library/pathlib.html#pathlib.Path.is_dir">q.is_dir()</a>
False

4
这是唯一明智的答案。如果你被迫使用Python 2,只需pip install pathlib2并使用后移版本即可。 - Navin
5
这个解决方案不会受到末尾os.sep的影响! - Dylan F
1
你不必真的“担心现有代码在使用Pathlib对象时会生成错误”,因为你可以简单地包装pathlib对象:path_as_string = str(Path(<somepath>)) - John

49
import os
p = os.path.abspath('..')

C:\Program Files ---> C:\\\

C:\ ---> C:\\\


13
这只获取当前工作目录的父目录,而不是OP所要求的任意路径的父目录。 - Sergio
在您的URL末尾添加双点,它就会起作用。例如: os.path.abspath(r'E:\O3M_Tests_Embedded\branches\sw_test_level_gp\test_scripts\..\..')结果为: E:\\O3M_Tests_Embedded\\branches - Arindam Roychowdhury
这个符号表示:/ - loretoparisi

36

@kender提供的另一种解决方案。

import os
os.path.dirname(os.path.normpath(yourpath))

其中yourpath是您想要获取父级的路径。

但是,该解决方案并不完美,因为它无法处理yourpath为空字符串或点的情况。

另一种解决方案将更好地处理这种特殊情况:

import os
os.path.normpath(os.path.join(yourpath, os.pardir))

以下是能够找到的每种情况的输出(输入路径为相对路径):

os.path.dirname(os.path.normpath('a/b/'))          => 'a'
os.path.normpath(os.path.join('a/b/', os.pardir))  => 'a'

os.path.dirname(os.path.normpath('a/b'))           => 'a'
os.path.normpath(os.path.join('a/b', os.pardir))   => 'a'

os.path.dirname(os.path.normpath('a/'))            => ''
os.path.normpath(os.path.join('a/', os.pardir))    => '.'

os.path.dirname(os.path.normpath('a'))             => ''
os.path.normpath(os.path.join('a', os.pardir))     => '.'

os.path.dirname(os.path.normpath('.'))             => ''
os.path.normpath(os.path.join('.', os.pardir))     => '..'

os.path.dirname(os.path.normpath(''))              => ''
os.path.normpath(os.path.join('', os.pardir))      => '..'

os.path.dirname(os.path.normpath('..'))            => ''
os.path.normpath(os.path.join('..', os.pardir))    => '../..'

输入路径是绝对路径(Linux路径):

os.path.dirname(os.path.normpath('/a/b'))          => '/a'
os.path.normpath(os.path.join('/a/b', os.pardir))  => '/a'

os.path.dirname(os.path.normpath('/a'))            => '/'
os.path.normpath(os.path.join('/a', os.pardir))    => '/'

os.path.dirname(os.path.normpath('/'))             => '/'
os.path.normpath(os.path.join('/', os.pardir))     => '/'

规范化路径是一种良好的实践,特别是在进行跨平台工作时。 - DevPlayer
@Maxim 这个解决方案并不完美,我已经进行了改进,因为原始解决方案无法处理一个情况。 - benjarobin

20
os.path.split(os.path.abspath(mydir))[0]

这对于指向目录的路径是行不通的,它只会再次返回该目录。 - Anthony Briggs
2
@AnthonyBriggs,我刚刚在Ubuntu 12.04上使用Python 2.7.3尝试了一下,它似乎可以正常工作。 os.path.split(os.path.abspath("this/is/a/dir/"))[0] 返回预期的 '/home/daniel/this/is/a'。我目前没有可用的Windows机器来检查。您在何种设置中观察到您所报告的行为? - Dan Menes
你可以使用parentdir = os.path.split(os.path.apspath(dir[:-1]))[0]。我确信这个方法是可行的,因为如果路径末尾有斜杠,则会被删除;如果没有斜杠,这个方法仍然有效(即使路径的最后一部分只有一个字符),因为前面有斜杠。当然,这假设路径是正确的,而不是像/a//b/c///d////这样的路径(在Unix中仍然是有效的),但大多数情况下它们是正确的,特别是当你使用os.path.abspath或任何其他os.path函数时。 - dylnmc
此外,为了抵消末尾的许多斜杠,您可以编写一个小型for循环来删除它们。我相信甚至可能有一个巧妙的一行代码来完成这个任务,或者将其与os.path.split结合在一行中完成。 - dylnmc
@Den Menes,我刚看到你的评论。如果你有像os.path.split("a/b//c/d///")这样的东西,那么它是不起作用的,例如cd //////dev//////等同于cd /dev/cd /dev;所有这些在Linux中都是有效的。我刚想出了这个方法,可能会有用:os.path.split(path[:tuple(ind for ind, char in enumerate(path) if char != "/" and char != "\\")[-1]])[0]。(这本质上是搜索最后一个非斜杠,并获取路径子字符串直到该字符。)我使用了path = "/a//b///c///d////",然后运行了上述语句,得到了'/a//b///c' - dylnmc
尽管上面的代码只是为了演示,但我仍然建议不要使用“dir”作为变量或引用。因为它也是一个内置函数。 - DevPlayer

16
os.path.abspath(os.path.join(somepath, '..'))

请注意:

import posixpath
import ntpath

print ntpath.abspath(ntpath.join('C:\\', '..'))
print ntpath.abspath(ntpath.join('C:\\foo', '..'))
print posixpath.abspath(posixpath.join('/', '..'))
print posixpath.abspath(posixpath.join('/home', '..'))

不支持符号链接。 - CodingTil

10
>>> import os
>>> os.path.basename(os.path.dirname(<your_path>))

例如在Ubuntu中:
>>> my_path = '/home/user/documents'
>>> os.path.basename(os.path.dirname(my_path))
# Output: 'user'

例如在Windows中:
>>> my_path = 'C:\WINDOWS\system32'
>>> os.path.basename(os.path.dirname(my_path))
# Output: 'WINDOWS'

以下两个例子均在 Python 2.7 中尝试


10
import os
print"------------------------------------------------------------"
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
print("example 1: "+SITE_ROOT)
PARENT_ROOT=os.path.abspath(os.path.join(SITE_ROOT, os.pardir))
print("example 2: "+PARENT_ROOT)
GRANDPAPA_ROOT=os.path.abspath(os.path.join(PARENT_ROOT, os.pardir))
print("example 3: "+GRANDPAPA_ROOT)
print "------------------------------------------------------------"

9
假设我们有以下目录结构:

1]

/home/User/P/Q/R

我们希望从目录R访问“P”的路径,然后可以使用以下方法访问:
ROOT = os.path.abspath(os.path.join("..", os.pardir));

2]

/home/User/P/Q/R

我们希望从目录R中访问“Q”目录的路径,这样我们就可以使用:
ROOT = os.path.abspath(os.path.join(".", os.pardir));

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