将pathlib的relative_to用于同一级别的目录

时间:2016-06-28 18:20:26

标签: python pathlib

python库pathlib提供Path.relative_to。如果一个路径是另一个路径的子路径,则此函数可以正常工作,如下所示:

In [12]: from pathlib import Path
In [13]: foo = Path("C:\\foo")
In [14]: bar = Path("C:\\foo\\bar")
In [15]: bar.relative_to(foo)
Out[15]: WindowsPath('bar')

但是,如果两个路径位于相同级别,则relative_to不起作用。

In [16]: baz = Path("C:\\baz")
In [17]: foo.relative_to(baz)
--------------------------------------------------------------------------
ValueError: 'C:\\foo' does not start with 'C:\\baz'

我希望结果是

WindowsPath("..\\baz")

函数os.path.relpath正确执行此操作:

In [18]: import os
In [19]: foo = "C:\\foo"
In [20]: bar = "C:\\bar"
In [21]: os.path.relpath(foo, bar)
Out[21]: '..\\foo'

有没有办法使用os.path.relpath来实现pathlib.Path的功能?

2 个答案:

答案 0 :(得分:6)

第一部分解决了OP的问题,但是如果像我一样,他真的想要相对于共同根的解决方案,那么第二部分就为他解决了这个问题。第三部分描述了我最初是如何接近它并为了利益而保留的。

相对路径

最近,与Python 3.4-6一样,os.path模块已扩展为接受pathlib.Path个对象。在下面的例子中,它不返回Path对象,并且强制一个包装结果。

foo = Path("C:\\foo")
baz = Path("C:\\baz")
Path(os.path.relpath(foo, baz))

> Path("..\\foo")

Common Path

我怀疑你是否真的在寻找一个相对于共同根的路径。如果是这种情况,则EOL中的以下内容更有用

Path(os.path.commonpath([foo, baz]))

> Path('c:/root')

通用前缀

在我开始os.path.commonpath之前,我曾使用os.path.comonprefix

foo = Path("C:\\foo")
baz = Path("C:\\baz")
baz.relative_to(os.path.commonprefix([baz,foo]))

> Path('baz')

但要预先警告你不应该在这种情况下使用它(见commonprefix : Yes, that old chestnut

foo = Path("C:\\route66\\foo")
baz = Path("C:\\route44\\baz")
baz.relative_to(os.path.commonprefix([baz,foo]))

> ...
> ValueError : `c:\\route44\baz` does not start with `C:\\route`

,而是来自J. F. Sebastian的以下内容。

Path(*os.path.commonprefix([foo.parts, baz.parts]))

> Path('c:/root')

......或者如果你感到啰嗦......

from itertools import takewhile
Path(*[set(i).pop() for i in (takewhile(lambda x : x[0]==x[1], zip(foo.parts, baz.parts)))])

答案 1 :(得分:3)

这让我很烦,所以这是一个仅基于pathlib的版本,我认为它可以完成os.path.relpath的工作。

def relpath(path_to, path_from):
    path_to = Path(path_to).resolve()
    path_from = Path(path_from).resolve()
    try:
        for p in (*reversed(path_from.parents), path_from):
            head, tail = p, path_to.relative_to(p)
    except ValueError:  # Stop when the paths diverge.
        pass
    return Path('../' * (len(path_from.parents) - len(head.parents))).joinpath(tail)