任务是实现一个函数root_relative_path(root : str, path : str) -> str
,它计算相对于根的相对路径,没有中间..
超出root
。例如root_relative_path('/abc', '/../def') == '/abc/def'
这个问题不同于Python中如何计算相对路径?因为在这种情况下root_relative_path(root='/tmp/abc', path='/../def')
应该返回/tmp/abc/def
而不是/tmp/def
。
import os.path
def root_relative_path(root : str, path : str) -> str:
return (os.path.join(root,
os.path.join(os.sep, os.path.normpath(path)).split(os.sep)[1:])))
我能够使用posixpath和pathlib模块的组合来实现root_relative_path函数。结果是
- 独立于平台(只要根路径对应于当前平台(
- 路径可以从
/
、./
或../
开始 - 并且将使用包括解析
..
s的normpath函数所涵盖的所有技术来对路径进行归一化
;
from pathlib import Path
import posixpath
def root_relative_path(root : str, path : str) -> str:
''' Calculates the relative path with respect to the root.
Any ".."s in the relative path will be resolved,
but not in a way that would cause the relative path to go beyond the root. '''
# posixpath.normpath doesn't work as expected if the path doesn't start with a slash, so we make sure it does
if not path.startswith('/'):
path = '/' + path
# The normalization process includes resolving any ".."s
# we're only using posixpath for the relative portion of the path, the outcome will still be platform independent
path = posixpath.normpath(path)
# Remove the beginning slash so that we're not trying to join two absolute paths
path = path[1:]
joined = Path(root) / path
# Per the OPs requirements the result needed to be a string,
# But if you're allowed to use Pathlib objects, you could just return joined without converting it to a string
return str(joined)
这个怎么样:
from pathlib import Path
def get_relative_path(path_from: Path, path_to: Path) -> Path:
"""
Calculate and return a relative path between the `path_from` and
`path_to` paths. Both paths must be absolute paths!
"""
if not (path_from.is_absolute() and path_to.is_absolute()):
raise ValueError('One or both of the passed paths are not absolute.')
items_from = path_from.parts
items_to = path_to.parts
while items_from[0] == items_to[0]:
items_from = items_from[1:]
items_to = items_to[1:]
return Path(*('..' for x in range(1, len(items_from))), *items_to)