Boost::Python- 可以从 dict 自动转换 -->标准::地图?

我有一个 C++ 类,它的成员函数可以接受从小到大的参数.让我们将这些参数命名为 a-f.所有参数都有默认值.作为我正在处理的 python 项目的一部分,我想将这个类公开给 python.目前,成员函数看起来像这样:

I've got a C++ class, with a member function that can take a small-to-large number of parameters. Lets name those parameters, a-f. All parameters have default values. As a part of the python project I am working on, I want to expose this class to python. Currently, the member function looks something like this:

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 来完成此操作:

Ideally, in Python, I would like to accomplish this using a dict:

>>> A = myModule.myClass();
>>> A.SetParameters({'a': 2.2, 'd': 4.3, b: '9.3'})

这样,用户可以按任何顺序输入值,并输入任意数量的值以被覆盖.关于如何在 boost::python 中实现这一点的任何想法?在我看来,我可以通过将地图输入更改为 boost::python 对象并使用提取函数来做到这一点.但是,这需要我更改库的接口(我更愿意保留 std::map 接口,并为 python 版本提供一些中间/自动转换技术).想法?

In this way, the user could enter the values in any order, and enter any number of them to be over-ridden. Any thoughts on how this could be accomplished in boost::python? It seems to me that I can do this via changing the map input to a boost::python object, and using the extract functions. However, this would require me to change the interface of my library (I'd prefer to keep the std::map interface, and have some intermediary/auto conversion technique for the python version). Thoughts?

推荐答案

我认为有几种方法比编写自己的转换器更容易完成.您可以使用 boost::python 的 map_indexing_suite 为您进行转换,也可以在 python 中使用关键字参数.我个人更喜欢关键字参数,因为这是更Pythonic"的方式.

I think there's a couple of ways that are easier to accomplish than writing your own converter. You can use boost::python's map_indexing_suite to do the conversion for you, or you can use keyword arguments in python. I personally prefer keyword arguments, as this is the more "Pythonic" way to do this.

这是你的课程(我为地图添加了 typedef):

So this is your class (I added a typedef for the map):

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 的示例:

Example using 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 包装器:

Example using keyword arguments. This requires using a raw_function wrapper:

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 中编写类似这样的东西:

this allows you to write stuff like this in Python:

A.SetParameters(a = 2.2, d = 4.3, b = 9.3)

相关文章