我实现了一堆函数,它们是从Python解释器调用的同一个C函数调度的:
PyObject *
CmdDispatch(PyObject *self, PyObject *args, PyObject *kwargs)
出乎意料的是,self为NULL,我需要获取当前正在调用的函数名称。有没有办法获得这些信息?
我有很多功能都在经历这个例行程序。此命令将所有选项处理为C ++映射,并将其传递给每个命令的实现。
更新: http://docs.python.org/extending/extending.html#a-simple-example具体说“self参数指向模块级别函数的模块对象;对于一个指向对象实例的方法。”但是我在链接python 2.6时传递null。
答案 0 :(得分:4)
我一直在努力解决非常类似的问题。
我得出的结论表明,无法在Python C API级别确定当前函数或调用者的名称。原因是,Python解释器只调用堆栈纯Python函数(用Python本身实现)。无论是否在模块方法表中注册,用C实现的函数都不会放在Python堆栈上,因此无法找到它们检查堆栈帧。
这是Python中的一个简单示例,说明了我想要实现的目标(我假设Juan要求类似的行为):
import sys
def foo():
print('foo:', sys._getframe(0).f_code.co_name)
def bar():
print('bar:', sys._getframe(0).f_code.co_name)
foo()
bar()
这是与此示例的近似等效(基于Python 3 docs),但使用Python C API实现:
// Based on example from Python 3.2.1 documentation, 5.4. Extending Embedded Python
// http://docs.python.org/release/3.2.1/extending/embedding.html#extending-embedded-python
//
#include <Python.h>
#include <frameobject.h>
static void foo()
{
PyThreadState * ts = PyThreadState_Get();
PyFrameObject* frame = ts->frame;
while (frame != 0)
{
char const* filename = _PyUnicode_AsString(frame->f_code->co_filename);
char const* name = _PyUnicode_AsString(frame->f_code->co_name);
printf("foo: filename=%s, name=%s\n", filename, name);
frame = frame->f_back;
}
}
static void bar()
{
PyRun_SimpleString(
"import sys\n"
"print(\"bar: filename=%s, name=%s\" % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_code.co_name))"
);
}
static PyObject* emb_numargs(PyObject *self, PyObject *args)
{
foo();
bar();
PyRun_SimpleString(
"import sys\n"
"print(\"emb_numargs: filename=%s, name=%s\" % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_code.co_name))"
);
return PyLong_FromLong(0);
}
static PyMethodDef EmbMethods[] = {
{"numargs", emb_numargs, METH_VARARGS, "Return number 0"},
{NULL, NULL, 0, NULL}
};
static PyModuleDef EmbModule = {
PyModuleDef_HEAD_INIT, "emb", NULL, -1, EmbMethods,
NULL, NULL, NULL, NULL
};
static PyObject* PyInit_emb(void)
{
return PyModule_Create(&EmbModule);
}
int main(int argc, char* argv[])
{
PyImport_AppendInittab("emb", &PyInit_emb);
Py_Initialize();
PyRun_SimpleString(
"import emb\n"
"print('This is Zero: ', emb.numargs())\n"
);
Py_Finalize();
return 0;
}
我希望这也完成了奈德的回答。
答案 1 :(得分:2)
Python api不是为了告诉你它正在调用什么函数而构建的。你已经创建了一个函数,它正在调用它,API假设你知道你编写了什么函数。您需要为每个Python函数创建一个小包装器函数。执行此操作的最佳方法是将一个C函数注册为一个Python函数,该函数将字符串作为其第一个参数。然后,在Python层中,根据需要创建尽可能多的Python函数,每个函数都使用正确的字符串参数调用C函数,以识别您真正想要调用的函数。
另一种选择是重新考虑C代码的结构,并拥有所需的函数,每个函数都调用您的公共帮助程序代码来处理选项等。
答案 2 :(得分:2)
注意为了清楚起见,未提供API错误检查;
此示例直接在python __ builtin __ 模块上插入一个新函数,允许在没有class.method模式的情况下调用该方法。只需将 mymodule 更改为任何其他模块即可。
PyObject* py_cb(PyObject *self, PyObject *args)
{
const char *name = (const char *) PyCObject_AsVoidPtr(self);
printf("%s\n", name);
Py_RETURN_NONE;
}
int main(int argc, char *argv)
{
PyObject *mod, *modname, *dict, *fnc, *usrptr;
const char *mymodule = "__builtin__";
PyMethodDef *m;
const char *method = "hello_python";
Py_Initialize();
mod = PyImport_ImportModule(mymodule);
modname = PyString_FromString(mymodule);
dict = PyModule_GetDict(mod);
m = (PyMethodDef *) calloc(1, sizeof(PyMethodDef));
m->ml_name = strdup(method);
m->ml_meth = py_cb;
usrptr = PyCObject_FromVoidPtr("I'm am the hello_python!", NULL);
fnc = PyCFunction_NewEx(m, usrptr, modname);
PyDict_SetItemString(dict, method, fnc);
...
当python脚本执行 hello_python 时,py_cb扩展函数将显示:
我是hello_python!
self 用于发送一个真实的指针,例如库上下文而不是此示例中的 const char * ,现在将其更改为虽然有点有趣。
答案 3 :(得分:0)
我不知道是否可以直接从C-API完成。在最坏的情况下,您可以从C调用traceback
模块,以获取调用者的名称。