在Python中获取调用者的相对路径

4

I've got this function:

def relative_path(*paths):
    return os.path.join(os.path.dirname(__file__), *paths)

如何将其更改为返回相对于调用者的路径?

例如,如果我从另一个脚本中调用relative_path('index.html'),是否可能从隐式地调用它的脚本获取与脚本相关的路径,还是需要修改relative_path以传递__file__,像这样?

def relative_path(__file__, *paths):
    return os.path.join(os.path.dirname(__file__), *paths)

这些路径是否严格以调用者目录为开头? - dmg
你是在询问如何确定调用者所在的脚本路径吗?如果不是,那么调用者的路径是什么? - martineau
我不知道如何更清楚地表达“获取调用者的相对路径”。您能否说明一下为什么这不清楚呢? - l0b0
为什么需要使用神奇的父文件位置呢?为什么不更明确地说明路径的基础是什么?Flask 要求您使用当前模块名称 (app = Flask(__name__)) 创建 Flask() 对象,以便它可以查找路径来加载模板 (sys.modules[name_passed_in].__file__)。这使其更加明确,也允许您覆盖路径。无需进行魔法调用程序查找。 - Martijn Pieters
2个回答

3

将解决方案适应于在Python中获取调用函数模块的__name__

file1.py

import os
import inspect

def relative_path(*paths):
    return os.path.join(os.path.dirname(__file__), *paths)

def relative_to_caller(*paths):
    frm = inspect.stack()[1]
    mod = inspect.getmodule(frm[0])
    return os.path.join(os.path.dirname(mod.__file__), *paths)

if __name__ == '__main__':
    print(relative_path('index.html'))

sub/sub_file.py

import sys
sys.path.append(r'/Users/xx/PythonScripts/!scratch')

import file1

if __name__ == '__main__':
    print(file1.relative_path('index.html'))
    print(file1.relative_to_caller('index.html'))

运行 sub_file.py 会产生以下输出:
/Users/xx/PythonScripts/!scratch/index.html
/Users/xx/PythonScripts/!scratch/sub/index.html

在上面链接的问题评论中有一些需要注意的地方...



0
请注意,这里可能会有一些追踪堆栈的可能性,但它可能会引起一些严重的问题(例如混淆“垃圾回收器”,或者甚至在某些情况下无法正常工作)。
我认为最干净的方法是将调用者传递给rel_path函数。
然而,正如您所知,Python通常有一种丑陋的做事方式。例如,您可以像这样做:
考虑以下两个脚本:
# relpath.py

import os


def rel_path(path):
    if os.path.isfile(__name__):
        return os.path.relpath(path, start=__name__)

    print("Warning: %s is not a file: returning path relative to the current working dir" % __name__, file=sys.stderr)
    return os.path.relpath(path)


# caller.py

import importlib.util


spec = importlib.util.spec_from_file_location(name=__file__, location="/workspace/relpath.py")

rel =  importlib.util.module_from_spec(spec)

spec.loader.exec_module(rel)
print(rel.rel_path("/tmp"))

我们在这里做了什么:使用importlib.util加载模块时,我们传递了name=__file__,这使得我们的模块名称由调用脚本的路径组成。因此,我们不需要将其作为参数传递给relpath.py

请注意,这不是一个干净的解决方案,可能对未来阅读您代码的开发人员来说不够可读。我只是想展示Python的可能性。


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