我有一个C ++类,其成员函数可以使用少量到大量的参数。让我们命名那些参数,a-f。所有参数都有默认值。作为我正在研究的python项目的一部分,我想将这个类暴露给python。目前,成员函数看起来像这样:
class myClass {
public:
// Constructors - set a-f to default values.
void SetParameters(std::map<std::string, double> &);
private:
double a, b, c, d, e, f;
}
void myClass::SetParameters(std::map<std::string, double> const& params) {
// Code to iterate over the map, and set any found key/value pairs to their
// corresponding variable. i.e.- "a" --> 2.0, would set myClass::a to 2.0
}
理想情况下,在Python中,我想使用dict完成此操作:
>>> A = myModule.myClass();
>>> A.SetParameters({'a': 2.2, 'd': 4.3, b: '9.3'})
通过这种方式,用户可以按任何顺序输入值,并输入任意数量的值以进行覆盖。有关如何在boost :: python中实现这一点的任何想法?在我看来,我可以通过将地图输入更改为boost :: python对象,并使用提取函数来实现此目的。但是,这需要我更改我的库的接口(我更喜欢保留std :: map接口,并为python版本提供一些中间/自动转换技术)。想法?
答案 0 :(得分:23)
我认为有两种方法比编写自己的转换器更容易实现。您可以使用boost :: python的map_indexing_suite为您进行转换,或者您可以在python中使用关键字参数。我个人更喜欢关键字参数,因为这是更“Pythonic”的方式。
所以这是你的类(我为地图添加了一个typedef):
typedef std::map<std::string, double> MyMap;
class myClass {
public:
// Constructors - set a-f to default values.
void SetParameters(MyMap &);
private:
double a, b, c, d, e, f;
};
使用map_indexing_suite的示例:
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
using boost::python;
BOOST_PYTHON_MODULE(mymodule)
{
class_<std::map<std::string, double> >("MyMap")
.def(map_indexing_suite<std::map<std::wstring, double> >() );
class_<myClass>("myClass")
.def("SetParameters", &myClass::SetParameters);
}
使用关键字参数的示例。这需要使用raw_function包装器:
using namespace boost::python;
object SetParameters(tuple args, dict kwargs)
{
myClass& self = extract<myClass&>(args[0]);
list keys = kwargs.keys();
MyMap outMap;
for(int i = 0; i < len(keys); ++i) {
object curArg = kwargs[keys[i]];
if(curArg) {
outMap[extract<std::string>(keys[i])] = extract<double>(kwargs[keys[i]]);
}
}
self.SetParameters(outMap);
return object();
}
BOOST_PYTHON_MODULE(mymodule)
{
class_<myClass>("myClass")
.def("SetParameters", raw_function(&SetParameters, 1));
}
这允许你在Python中编写这样的东西:
A.SetParameters(a = 2.2, d = 4.3, b = 9.3)
答案 1 :(得分:8)
This blog post对如何编写这些转换器有一个非常明确的描述。基本模式是定义一个具有以下形式的类:
struct SomeType_from_PyObject
{
SomeType_from_PyObject();
static void* convertible(PyObject* obj_ptr);
static void construct(PyObject* obj_ptr,
converter::rvalue_from_python_stage1_data* data);
};
构造函数负责将此转换器添加到Boost.Python的注册表中:
SomeType_from_PyObject::SomeType_from_PyObject()
{
converter::registry::push_back(&convertible,
&construct,
type_id<SomeType>());
}
函数convertible
告诉Boost这个转换器是否可以转换指定的Python对象:
void* SomeType_from_PyObject::convertible(PyObject* obj_ptr)
{
if (PyMapping_Check(obj_ptr)) {
return obj_ptr;
} else {
return NULL;
}
}
construct
函数实际上创建了转换类型的对象:
void SomeType_from_PyObject::construct(PyObject* obj_ptr,
converter::rvalue_from_python_stage1_data* data)
{
typedef converter::rvalue_from_python_storage<SomeType> storage_t;
storage_t* the_storage = reinterpret_cast<storage_t*>(data);
void* memory_chunk = the_storage->storage.bytes;
object obj(handle<>(borrowed(obj_ptr)));
SomeType* output = new (memory_chunk) SomeType();
// Use the contents of obj to populate output, e.g. using extract<>
data->convertible = memory_chunk;
}
然后在你的BOOST_PYTHON_MODULE里面,包含行
SomeType_from_PyObject();
答案 2 :(得分:0)
我只是用boost :: python弄湿我的脚趾,所以无法完全回答你的问题。但我看到的第一个障碍是保证py dict的键都是字符串。 Python dicts也可以键入元组(我假设更多类型)。