raef
raef

Reputation: 51

How to set a new attribute in pybind11 to python object within c++?

I am trying to create a new python class in c++ with pybind11, fill it with data and then return it.

For example, take a simple class in python, which is then used in a c++ function call compiled with pybind11.

from TEST import func_wrapper 

class class_DATA_py:
    def __init__(self):
        self.a   = 1

data_class_input = class_DATA_py()
data_return      = func_wrapper(data_class_input) #is not compiling
print(data_return.new_data) #is what I wish to see

Now use the class in c++ with the help of pybind 11.

#include <pybind11/pybind11.h>
namespace py = pybind11;

py::object func_wrapper(py::object data_class_input){

    //1) read in the data of some python class
    int a       = data_class_input.attr("a").cast<int>();  

    //2) do something 
    float new_data = 3.1; //just an example value

    //3) make the results accessible from python:
    py::object data_class_output;
    //This is where things go wrong and crashes during compilation
    //Is there a way to set an attribute so that I can write something like below?
    data_class_output.attr("new_data")    = py::cast(new_data);
    return data_class_output;
}

//pybind11-related:
PYBIND11_MODULE(TEST,m){
  m.doc() = "pybind11 example plugin";
  m.def("func_wrapper",  &func_wrapper);
  );

The idea is then to be able to use the data_class_output in python and access for example the data_class_output.new_data value.

A workaround is defining the data_class_output class also in python, use it as an additional input and fill the values there then.

However, is it possible to work in the way outlined above and define the "py::object data_class_output" from scratch in c++ and then use it in python without problems?

Upvotes: 0

Views: 4902

Answers (1)

Wim Lavrijsen
Wim Lavrijsen

Reputation: 3788

There needs to be some object instance to attach the new attribute to. This object, if you want a new one, will need to be created from some type. If based on the discussion above you want the output type and input type to be the same, you can get the proper type from the __class__ attribute of the input. Then, assuming __init__ takes no additional parameters, create an instance and attach the attribute.

Working example by adjusting yours follows:

#include <pybind11/pybind11.h>

namespace py = pybind11;

py::object func_wrapper(py::object data_class_input){

    //1) read in the data of some python class
    int a       = data_class_input.attr("a").cast<int>();

    //2) do something
    float new_data = 3.1; //just an example value

    //3) make the results accessible from python:
    py::object data_class_input_type = data_class_input.attr("__class__");
    py::object data_class_output = data_class_input_type();
    data_class_output.attr("new_data")    = py::cast(new_data);

    return data_class_output;
}

//pybind11-related:
PYBIND11_MODULE(TEST,m){
  m.doc() = "pybind11 example plugin";
  m.def("func_wrapper",  &func_wrapper);
}

after which your python snippet will print:

3.09999990463

Upvotes: 3

Related Questions