简体   繁体   English

您可以通过pybind11创建python类吗?

[英]Can you create a python class via pybind11?

Currently when working with python + pybind11 I find it frustrating to work with the typed c++ classes/structs. 目前,当使用python + pybind11时,我发现使用类型化的c ++类/结构令人沮丧。

I would like to change my bindings so that they generate a simple python class, with an __init__ and a simple function like shown below. 我想更改绑定,以便它们生成具有__init__和如下所示的简单函数的简单python类。 Is something like the feasible? 可行吗?

Reasoning: 推理:
I currently have a struct that I generate via c++, but it has a lot of heavy std::vector<float> s that I would like to pass to python, and keep as numpy arrays inside a similar interfacing python class. 我目前有一个通过c ++生成的结构,但是它有很多重的std::vector<float> ,我想传递给python,并以numpy数组的形式保存在类似的接口python类中。 (bonus points if you can tell me how to move vectors to be numpy arrays quickly!) (如果您能告诉我如何快速将向量移动为numpy数组,则可以加分!)

I have already completely bound my c++ struct with pybind11, so I feel like I know what I'm doing... however I can't seem to figure out if this is possible! 我已经用pybind11完全绑定了我的c ++结构,所以我觉得我知道自己在做什么...但是我似乎不知道这是否可行!

So, as a learning exercise, can I make the following python class via pybind11? 因此,作为一项学习练习,我可以通过pybind11创建以下python类吗?

>>> python
class MyStruct:
    def __init__(self, A_in, descriptor_in):
        self.A = A_in
        self.descriptor = descriptor_in

    def add_to_vec(f_in):
        self.A.append(f_in)
<<< python

Edit: I want to say I 'think' that this is doable with the python C api, but I'd like to avoid using that directly if I can. 编辑:我想说我“认为”这对于python C api是可行的,但是我想避免直接使用它。 (but if you think that's the only way, please let me know :) ) (但是,如果您认为这是唯一的方法,请告诉我:))

Edit2: (response to @Erwan) Edit2 :(响应@Erwan)
The only way I'm aware of to get class variables individually is this (shown below). 我知道的单独获取类变量的唯一方法是这种方式(如下所示)。 You cannot use the pybind advertised buffer_protocol interface if you have more than one numpy array in the struct you would like to get. 如果要在结构中包含多个numpy数组,则不能使用pybind公告的buffer_protocol接口。 However this requires creating a python-interface only function .def (not ideal) that points to (what I think is a copy) of the original data (so it's probably slow, i haven't benchmarked it, but I'm not sure if this was is a hack or the correct way to get vectors into numpy arrays). 但是,这需要创建一个仅python接口的函数.def (不理想),它指向原始数据的(我认为是副本)(所以它可能很慢,我尚未对其进行基准测试,但是我不确定(如果这是hack或将向量放入numpy数组的正确方法)。

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <vector>
#include <string>


struct Pet {
    Pet(const std::string &name) : name(name) { 
            bdata.push_back(22.);
            bdata.push_back(23.1);
            bdata.push_back(24.);
            bdata.push_back(2222.);
        }
    void setName(const std::string &name_) { name = name_; }
    const std::string &getName() const { return name; }

    std::string name;
    std::vector<float> bdata;
};


namespace py = pybind11;

PYBIND11_MODULE(example, m) {
    py::class_<Pet>(m, "Pet")
            .def(py::init<const std::string &>())
            .def("setName", &Pet::setName)
            .def("getName", &Pet::getName)
            .def("bdata", [](Pet &m) -> py::array {
                    py::buffer_info buff_info(py::buffer_info(
                            m.bdata.data(),                               /* Pointer to buffer */
                            sizeof(float),                          /* Size of one scalar */
                            py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
                            m.bdata.size()                                      /* Number of dimensions */
                    ));
                    return py::array(buff_info);
            });
}

I don't understand your question in whole, but I'll take this part: 我不完全理解您的问题,但是我将介绍以下内容:

bonus points if you can tell me how to move vectors to be numpy arrays quickly! 奖励积分,如果您可以告诉我如何快速将向量移动为numpy数组!

If you use the return result of bdata.data() combined with numpy.frombuffer() and bdata.size() if need be, you can get a view on the vector data, which is guaranteed to be contiguous as of C++11. 如果将bdata.data()的返回结果与numpy.frombuffer()和bdata.size()结合使用,则可以获取矢量数据视图,从C ++开始,该数据将保证是连续的11。 (The normal numpy.array() call will not honor copy=False in this case, but frombuffer acts like a cast.) Since there is no copy, that's probably as quick as it gets. (在这种情况下,正常的numpy.array()调用将不接受copy = False,但frombuffer的作用类似于强制类型转换。)由于没有副本,因此可能会尽快。

Below is an example in cppyy (which allows for easy testing, but the use of which is otherwise immaterial to the answer of how to mix std::vector and numpy.array per se). 下面是cppyy中的一个示例(该示例可以轻松进行测试,但是对于如何将std :: vector和numpy.array本身混合使用,答案的使用并不重要)。 The gravy is in the last few lines: the update to 'arr' will show up in the original vector (and vv) b/c frombuffer is a view, not a copy: 关键在于最后几行:'arr'的更新将显示在原始矢量(和vv)中,b / c frombuffer是视图,而不是副本:

import cppyy
import numpy as np

# load struct definition
cppyy.cppdef("""
struct Pet {
    Pet(const std::string &name) : name(name) {
        bdata.push_back(22.);
        bdata.push_back(23.1);
        bdata.push_back(24.);
        bdata.push_back(2222.);
    }
    void setName(const std::string &name_) { name = name_; }
    const std::string &getName() const { return name; }

    std::string name;
    std::vector<float> bdata;
};""")

# create a pet object
p = cppyy.gbl.Pet('fido')

print(p.bdata[0]) # for reference (prints 22, per above)

# create a numpy view on the std::vector's data
#   add count=p.bdata.size() if need be
arr = np.frombuffer(p.bdata.data(), dtype=np.float32)

# prove that it worked as intended
arr[0] = 14
print(p.bdata[0]) # shows update to 14
p.bdata[2] = 17.5
print(arr[2])     # shows update to 17.5

which will print: 它将打印:

22.0
14.0
17.5

'arr' may become invalid if the std::vector resizes. 如果调整std :: vector的大小,则“ arr”可能无效。 If you know the maximum size, however, and it is not too large or will be fully used for sure, you can reserve that, so the vector's internal data will not be reallocated. 但是,如果您知道最大大小,并且该大小不是太大或无法完全使用,则可以保留该大小,因此不会重新分配向量的内部数据。

Depending on how/where you store the numpy array, I also recommend tying the life time of 'p' (and hence 'p.bdata') to 'arr' eg. 根据存储numpy数组的方式/位置,我还建议将“ p”(因此也就是“ p.bdata”)的生存期绑定到“ arr”。 by keeping them both as data members in an instance of the wrapper class you're after. 通过将它们都保留为数据类,作为您所需要的包装器类的实例。

If you want to do the conversion in C++ instead, use PyArray_FromBuffer from NumPy's array API. 如果要使用C ++进行转换,请使用NumPy的数组API中的PyArray_FromBuffer。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM