從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'作爲第二個我想傳遞給它的對象。這符合我的希望 - 當我在一個事件中添加屬性時,它在我傳遞給另一個事件時仍然存在。也許不是最好的解決方案...但是,它的工作原理。 –