从C++的邮件列表中得到了这个奇妙的解决方案。
在C++类中实现std::map<std::string, boost::python::object>
,然后重载__getattr__()
和__setattr__()
来读取和写入std :: map。然后像往常一样将它发送到Python的boost::python::ptr()
,不需要在C++端保留一个对象或发送一个到python。它完美的作品。
编辑:我也发现我不得不以一种特殊的方式覆盖__setattr__()
函数,因为它破坏了我添加的add_property()
。这些东西在得到它们的时候工作得很好,因为python在调用__getattr__()
之前检查了一个类的属性,但是__setattr__()
没有这样的检查。它只是直接调用它。所以我必须做出一些改变才能把它变成一个完整的解决方案。下面是完整的实施解决方案:
首先创建一个全局变量:
boost::python::object PyMyModule_global;
创建一个类,如下所示(以要添加到它的任何其他信息):
class MyClass
{
public:
//Python checks the class attributes before it calls __getattr__ so we don't have to do anything special here.
boost::python::object Py_GetAttr(std::string str)
{
if(dict.find(str) == dict.end())
{
PyErr_SetString(PyExc_AttributeError, JFormat::format("MyClass instance has no attribute '{0}'", str).c_str());
throw boost::python::error_already_set();
}
return dict[str];
}
//However, with __setattr__, python doesn't do anything with the class attributes first, it just calls __setattr__.
//Which means anything that's been defined as a class attribute won't be modified here - including things set with
//add_property(), def_readwrite(), etc.
void Py_SetAttr(std::string str, boost::python::object val)
{
try
{
//First we check to see if the class has an attribute by this name.
boost::python::object obj = PyMyModule_global["MyClass"].attr(str.c_str());
//If so, we call the old cached __setattr__ function.
PyMyModule_global["MyClass"].attr("__setattr_old__")(ptr(this), str, val);
}
catch(boost::python::error_already_set &e)
{
//If it threw an exception, that means that there is no such attribute.
//Put it on the persistent dict.
PyErr_Clear();
dict[str] = val;
}
}
private:
std::map<std::string, boost::python::object> dict;
};
然后定义python模块,如下所示,添加任何其他的defs和你想要的属性:
BOOST_PYTHON_MODULE(MyModule)
{
boost::python::class_<MyClass>("MyClass", boost::python::no_init)
.def("__getattr__", &MyClass::Py_GetAttr)
.def("__setattr_new__", &MyClass::Py_SetAttr);
}
然后初始化蟒蛇:
void PyInit()
{
//Initialize module
PyImport_AppendInittab("MyModule", &initMyModule);
//Initialize Python
Py_Initialize();
//Grab __main__ and its globals
boost::python::object main = boost::python::import("__main__");
boost::python::object global = main.attr("__dict__");
//Import the module and grab its globals
boost::python::object PyMyModule = boost::python::import("MyModule");
global["MyModule"] = PyMyModule;
PyMyModule_global = PyMyModule.attr("__dict__");
//Overload MyClass's setattr, so that it will work with already defined attributes while persisting new ones
PyMyModule_global["MyClass"].attr("__setattr_old__") = PyMyModule_global["MyClass"].attr("__setattr__");
PyMyModule_global["MyClass"].attr("__setattr__") = PyMyModule_global["MyClass"].attr("__setattr_new__");
}
一旦你完成了这一切,你就可以坚持的变化在Python转移到C++进行的实例。任何在C++中定义为属性的东西都会被正确处理,任何不会被附加到dict
而不是类__dict__
。
找到了一个解决方案。在python中,我添加了一个函数: def ProcessEvent(event,obj): return globals()[event](obj.PyObj) 在C++中,我添加了一个'boost :: python :: object'给我的C++类命名'PyObj',该类初始化为'boost :: python :: ptr(this)',并用它来调用我的事件,传递我想调用的事件的名称作为第一个参数, boost :: python :: ptr'作为第二个我想传递给它的对象。这符合我的希望 - 当我在一个事件中添加属性时,它在我传递给另一个事件时仍然存在。也许不是最好的解决方案...但是,它的工作原理。 –