質問

I have a c++ boost python object (the boost wrapper on a PyObject *) for a Python list,

  PyObject * pyList = func(...);
  boost::python::object listObj(handle<>(boost::python::borrowed(pyList)));

I can verify this is indeed a list by performing operations on it such as

boost::python::object np = import("numpy");
boost::python::np_difference = np.attr("diff");
np_difference(listObj);
for(int i=0; i<len(listObj); i++){                                                                                         
  double value = boost::python::extract<double>(listObj[i]);                                                                              
  cout << i << " " << value << endl;                                                                                       
} 

Which subtracts the ith element from the i-1 element and creates a new list, each element of which is then extracted and printed to stdout in C++.

What I would like to do is use this listObj with the print function I define as

boost::python::object print = std.attr("print");    

But I would just like it to print one of the elements I specify. In python I would just write

print myList[0]

as it is simply a python list. But when I try

print(listObj[0]);

in c++ with boost python I get

Error in Python: <type 'exceptions.TypeError'>: No to_python (by-value) converter found for C++ type: boost::python::api::proxy<boost::python::api::item_policies>.

So how can I access a single element from a python list object and use it in a python call like the print method above (or any other python function that takes a string for input) from within c++?

役に立ちましたか?

解決

The boost::python::object's operator[] returns a boost::python::proxy object. While the proxy class has an implicit conversion to boost::python::object, there are many areas in the API where an explicit conversion is required.

Explicitly constructing a boost::python::object from the proxy should resolve the conversion exception:

print(boost::python::object(listObjString[0]));

Here is a complete embedded Python example demonstrating how to print a single element from a list via:

  • The Python builtin print function
  • The PyObject_Print() function in the Python/C API
  • Extracting the string representation of an object via __str__ and printing with std::cout
#include <boost/foreach.hpp>
#include <boost/python.hpp>
#include <boost/range/irange.hpp>

/// @brief Default flag to have PyObject_Print use the object's
///        __str__ method.  The python.h files only define the flag for
///        __repr__.
#define Py_PRINT_STR 0

int main()
{
  Py_Initialize();

  namespace python = boost::python;
  try
  {
    // Create and populate a Python list.
    // >>> list = [x for x in range(100, 103)]
    python::list list;    
    BOOST_FOREACH(int x, boost::irange(100, 103))
        list.append(x);

    // The proxy returned from a Boost.Python's operator[] provides a
    // user-defined conversion to a Boost.Python object.  In most cases,
    // explicitly invoking the conversion is required.

    // Print list[0] using the built-in function print.
    /// >>> getattr(__builtins__, 'print')(list[0])
    python::object print =
        python::import("__main__").attr("__builtins__").attr("print");
    print(python::object(list[0]));

    // Print list[1] using the Python/C API.
    // >>> import sys; sys.stdout.write(list[1].__str__())
    PyObject_Print(python::object(list[1]).ptr(), stdout, Py_PRINT_STR);
    std::cout << std::endl;

    // Print list[2] using the result of the object's __str__ method, and
    // extract a C++ string.
    std::cout << python::extract<std::string>(
                   python::object(list[2]).attr("__str__")())() << std::endl;

  }
  catch (python::error_already_set&)
  {
    PyErr_Print();
  }
}

Output:

100
101
102

他のヒント

Edit Tanner Sansbury answer is the C++ way to approach the question, the method I present below is simply a work around which allows you to assign a python object in the boost::python namespace to the __main__ python interpreter namespace.


Got it. Inspired from here, one needs to assign the boost::python::object to the python namespace, i.e.

    boost::python::object main_module = boost::python::import("__main__");
    boost::python::object main_namespace = main_module.attr("__dict__");
    main_module.attr("myList") = listObj;  
    boost::python::exec("print myList", main_namespace);  

Hope this helps.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top