简而言之,与嵌入式Python静态链接的Python扩展需要在初始化解释器之前将其模块初始化函数显式添加到初始化表中。
@H_301_3@PyImport_AppendInittab("hello", &inithello); Py_Initialize();
Boost.Python使用该BOOST_PYTHON_MODULE
宏定义python模块初始化程序。结果函数不是模块导入器。这种区别类似于创建@H_301_3@example.py模块和调用的区别@H_301_3@import example。
导入模块时,Python将首先检查该模块是否为内置模块。如果模块不存在,则Python将搜索模块搜索路径,以尝试根据模块名称查找python文件或库。如果找到了库,则Python期望该库提供一个将初始化模块的函数。找到后,导入将在modules表中创建一个空模块,然后对其进行初始化。对于静态链接的模块(例如@H_301_3@hello原始代码中的模块),模块搜索路径将无济于事,因为没有可查找的库。
对于嵌入,模块表和初始化函数文档指出,对于静态模块,除非初始化表中有条目,否则不会自动调用模块初始化器函数。对于Python 2和Python 3,可以通过调用PyImport_AppendInittab()
before来实现Py_Initialize()
:
@H_301_3@BOOST_PYTHON_MODULE(hello) { // ... } PyImport_AppendInittab("hello", &inithello); Py_Initialize(); // ... boost::python::object hello = boost::python::import("hello");
还要注意,Python的C API用于在Python 2和3之间嵌入更改的模块初始化函数的命名约定,因此,对于@H_301_3@BOOST_PYTHON_MODULE(hello),可能需要同时使用@H_301_3@&inithelloPython 2和@H_301_3@&PyInit_helloPython 3。
这是一个完整的示例,演示了将嵌入式Python导入@H_301_3@demo用户模块,然后再导入静态链接的@H_301_3@hello模块。它还在用户模块中调用一个函数,@H_301_3@demo.multiply然后该函数将调用通过静态链接的模块公开的方法。
@H_301_3@#include <cstdlib> // setenv, atoi #include <iostream> // cerr, cout, endl #include <boost/python.hpp> struct World { void set(std::string msg) { this->msg = msg; } std::string greet() { return msg; } std::string msg; }; /// Staticly linking a Python extension for embedded Python. BOOST_PYTHON_MODULE(hello) { namespace python = boost::python; python::class_<World>("World") .def("greet", &World::greet) .def("set", &World::set) ; } int main(int argc, char *argv[]) { if (argc < 3) { std::cerr << "Usage: call pythonfile funcname [args]" << std::endl; return 1; } char* module_name = argv[1]; char* function_name = argv[2]; // Explicitly add initializers for staticly linked modules. PyImport_AppendInittab("hello", &inithello); // Initialize Python. setenv("PYTHONPATH", ".", 1); Py_Initialize(); namespace python = boost::python; try { // Convert remaining args into a Python list of integers. python::list args; for (int i=3; i < argc; ++i) { args.append(std::atoi(argv[i])); } // Import the user requested module. // >>> import module python::object module = python::import(module_name); // Invoke the user requested function with the provided arguments. // >>> result = module.fn(*args) python::object result = module.attr(function_name)(*python::tuple(args)); // Print the result. std::cout << python::extract<int>(result)() << std::endl; } catch (const python::error_already_set&) { PyErr_Print(); return 1; } // Do not call Py_Finalize() with Boost.Python. }
@H_301_3@import hello planet = hello.World() planet.set('foo') def multiply(a,b): print planet.greet() print "Will compute", a, "times", b c = 0 for i in range(0, a): c = c + b return c
用法:
@H_301_3@$ ./a.out demo multiply 21 2 foo Will compute 21 times 2 42
在上面的代码中,我选择使用Boost.Python而不是Python / C API,并用等效的Python代码注释C ++注释。我发现它更加简洁,出错的可能性也大大降低。如果发生Python错误,Boost.Python将引发异常,并且所有引用计数都将得到适当处理。
另外,在使用Boost.Python时,请勿调用Py_Finalize()
。根据“嵌入- 入门”部分:
请注意,此时您不得呼叫@H_301_3@Py_Finalize()停止解释器。这可能会在boost.python的未来版本中修复。