对同级目录使用pathlib的relative_to
Using pathlib's relative_to for directories on the same level
python 图书馆 pathlib
provides Path.relative_to
。如果一条路径是另一条路径的子路径,则此函数可以正常工作,如下所示:
from pathlib import Path
foo = Path("C:\foo")
bar = Path("C:\foo\bar")
bar.relative_to(foo)
> WindowsPath('bar')
但是,如果两条路径在同一级别,relative_to
则不起作用。
baz = Path("C:\baz")
foo.relative_to(baz)
> ValueError: 'C:\foo' does not start with 'C:\baz'
我希望结果是
WindowsPath("..\baz")
函数 os.path.relpath
正确执行此操作:
import os
foo = "C:\foo"
bar = "C:\bar"
os.path.relpath(foo, bar)
> '..\foo'
有没有办法使用pathlib.Path
实现os.path.relpath
的功能?
第一部分解决了 OP 的问题,但如果像我一样,他真的想要相对于共同根的解决方案,那么第二部分会为他解决。第三部分描述了我最初是如何接近它的,为了兴趣而保留。
相对路径
最近,与 Python 3.4-6 一样,os.path
模块已扩展为接受 pathlib.Path
对象。然而,在以下情况下,它不会 return 一个 Path 对象,并且一个人被迫包装结果。
foo = Path("C:\foo")
baz = Path("C:\baz")
Path(os.path.relpath(foo, baz))
> Path("..\foo")
共同路径
我怀疑您实际上是在寻找相对于公共根的路径。如果是这种情况,来自 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`
而是来自 .
的下一个
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)))])
这让我很烦,所以这里有一个仅包含路径库的版本,我认为它可以完成 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)
pathlib 的 的递归版本。我发现这更具可读性,并且在大多数情况下它将首先 try
成功,因此它应该具有与原始 relative_to
函数相似的性能:
def relative(target: Path, origin: Path):
""" return path of target relative to origin """
try:
return Path(target).resolve().relative_to(Path(origin).resolve())
except ValueError as e: # target does not start with origin
# recursion with origin (eventually origin is root so try will succeed)
return Path('..').joinpath(relative(target, Path(origin).parent))
python 图书馆 pathlib
provides Path.relative_to
。如果一条路径是另一条路径的子路径,则此函数可以正常工作,如下所示:
from pathlib import Path
foo = Path("C:\foo")
bar = Path("C:\foo\bar")
bar.relative_to(foo)
> WindowsPath('bar')
但是,如果两条路径在同一级别,relative_to
则不起作用。
baz = Path("C:\baz")
foo.relative_to(baz)
> ValueError: 'C:\foo' does not start with 'C:\baz'
我希望结果是
WindowsPath("..\baz")
函数 os.path.relpath
正确执行此操作:
import os
foo = "C:\foo"
bar = "C:\bar"
os.path.relpath(foo, bar)
> '..\foo'
有没有办法使用pathlib.Path
实现os.path.relpath
的功能?
第一部分解决了 OP 的问题,但如果像我一样,他真的想要相对于共同根的解决方案,那么第二部分会为他解决。第三部分描述了我最初是如何接近它的,为了兴趣而保留。
相对路径
最近,与 Python 3.4-6 一样,os.path
模块已扩展为接受 pathlib.Path
对象。然而,在以下情况下,它不会 return 一个 Path 对象,并且一个人被迫包装结果。
foo = Path("C:\foo")
baz = Path("C:\baz")
Path(os.path.relpath(foo, baz))
> Path("..\foo")
共同路径
我怀疑您实际上是在寻找相对于公共根的路径。如果是这种情况,来自 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`
而是来自
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)))])
这让我很烦,所以这里有一个仅包含路径库的版本,我认为它可以完成 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)
pathlib 的 try
成功,因此它应该具有与原始 relative_to
函数相似的性能:
def relative(target: Path, origin: Path):
""" return path of target relative to origin """
try:
return Path(target).resolve().relative_to(Path(origin).resolve())
except ValueError as e: # target does not start with origin
# recursion with origin (eventually origin is root so try will succeed)
return Path('..').joinpath(relative(target, Path(origin).parent))