Python C Extension如何获取用户模块所在目录及当前工作目录
解决方案
首先明确核心误区:你要获取的是调用你扩展接口的用户模块所在目录,而非进程当前工作目录——后者是Python进程启动时的目录,和用户模块存储位置无直接关联,这也是你之前思路的主要偏差点。
Python C API 没有单独封装路径获取的专用C函数,但你可以直接通过C API调用Python原生的inspect、os.path模块实现需求,两种可行实现方案如下:
方案1:纯C扩展层面直接实现
核心逻辑是通过C API获取调用栈信息,拿到调用方模块的__file__属性后解析目录,示例代码如下:
#include <Python.h> #include <osdefs.h> static PyObject* get_caller_dir(void) { // 导入inspect模块 PyObject* inspect_mod = PyImport_ImportModule("inspect"); if (!inspect_mod) return NULL; // 调用inspect.stack()获取调用栈 PyObject* stack = PyObject_CallMethod(inspect_mod, "stack", NULL); if (!stack) { Py_DECREF(inspect_mod); return NULL; } // 取调用栈的第二层(第一层是当前C扩展函数本身,第二层是调用它的用户代码) PyObject* frame_info = PyList_GetItem(stack, 1); if (!frame_info) { Py_DECREF(stack); Py_DECREF(inspect_mod); return NULL; } PyObject* frame = PyTuple_GetItem(frame_info, 0); if (!frame) { Py_DECREF(stack); Py_DECREF(inspect_mod); return NULL; } // 获取frame的f_globals属性里的__file__ PyObject* f_globals = PyObject_GetAttrString(frame, "f_globals"); PyObject* file_path = PyDict_GetItemString(f_globals, "__file__"); if (!file_path) { Py_DECREF(f_globals); Py_DECREF(stack); Py_DECREF(inspect_mod); return NULL; } // 调用os.path.dirname获取目录 PyObject* os_mod = PyImport_ImportModule("os.path"); PyObject* dir_path = PyObject_CallMethod(os_mod, "dirname", "O", file_path); // 清理引用 Py_DECREF(os_mod); Py_DECREF(f_globals); Py_DECREF(stack); Py_DECREF(inspect_mod); return dir_path; }
拿到目录路径后,把用户传入的相对路径和目录拼接即可得到完整的图片路径。
方案2:增加Python层包装(更推荐)
C扩展只负责接收绝对路径加载图片,你额外提供一层Python封装接口,在Python层完成路径解析:
# 这是你随C扩展一起发布的Python包装模块 import os import inspect from your_c_ext import load_image as _load_image def load_image(img_path: str) -> Image: if not os.path.isabs(img_path): # 获取调用方模块所在目录 caller_frame = inspect.stack()[1] caller_module_path = caller_frame.filename caller_dir = os.path.dirname(os.path.abspath(caller_module_path)) img_path = os.path.join(caller_dir, img_path) return _load_image(img_path)
这个方案代码更易维护,出错概率更低,性能损耗完全可以忽略。
内容的提问来源于stack exchange,提问作者GreyHope
相关产品推荐
相关产品推荐

