【发布时间】:2020-02-13 10:09:37
【问题描述】:
我对创建可以在 Python 中使用的 C++ 类非常陌生。我在网上浏览了很多帖子。无论是在 StackOverflow、gist、github 上,......我也阅读了文档,但我不确定如何解决我的问题。
基本上,这个想法是这样做的:http://www.speedupcode.com/c-class-in-python3/
由于我想避免创建自己的python newtype 的负担,我认为使用上面示例中的PyCapsule_New 和PyCapsule_GetPointer 可能是一种解决方法,但也许我有误导性,我仍然需要创建复杂的数据类型。
这是我希望能够从 python 调用的类的标题:
template<typename T>
class Graph {
public:
Graph(const vector3D<T>& image, const std::string& similarity, size_t d) : img(image) {...}
component<T> method1(const int k, const bool post_processing=true);
private:
caller_map<T> cmap;
vector3D<T> img; // input image with 3 channels
caller<T> sim; // similarity function
size_t h; // height of the image
size_t w; // width of the image
size_t n_vertices; // number of pixels in the input image
size_t conn; // radius for the number of connected pixels
vector1D<edge<T>> edges; // graph = vector of edges
void create_graph(size_t d);
tuple2 find(vector2D<subset>& subsets, tuple2 i);
void unite(vector2D<subset>& subsets, tuple2 x, tuple2 y);
};
所以你可以看到我的类包含复杂的结构。 vector1D 只是 std::vector 但 edge 是由
template<typename T>
struct edge {
tuple2 src;
tuple2 dst;
T weight;
};
有些方法使用其他复杂的结构。
无论如何,我已经创建了自己的 Python 绑定。这里我只放相关功能。我创建了我的constructor 如下:
static PyObject *construct(PyObject *self, PyObject *args, PyObject *kwargs) {
// Arguments passed from Python
PyArrayObject* arr = nullptr;
// Default if arguments not given
const char* sim = "2000"; // similarity function used
const size_t conn = 1; // Number of neighbor pixels to consider
char *keywords[] = {
"image",
"similarity",
"d",
nullptr
};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|sI:vGraph", keywords, PyArray_Converter, &arr, &sim, &conn)) {
// Will need to DECRF(arr) somewhere?
return nullptr;
}
set<string> sim_strings = {"1976", "1994", "2000"};
if (sim_strings.find(sim) == sim_strings.end()) {
PyErr_SetString(PyExc_ValueError, "This similarity function does not exist");
Py_RETURN_NONE;
}
// Parse the 3D numpy array to vector3D
vector3D<float> img = parse_PyArrayFloat<float>(arr);
// call the Constructor
Graph<float>* graph = new Graph<float>(img, sim, conn);
// Create Python capsule with a pointer to the `Graph` object
PyObject* graphCapsule = PyCapsule_New((void * ) graph, "graphptr", vgraph_destructor);
// int success = PyCapsule_SetPointer(graphCapsule, (void *)graph);
// Return the Python capsule with the pointer to `Graph` object
// return Py_BuildValue("O", graphCapsule);
return graphCapsule;
}
在调试我的代码时,我可以看到我的构造函数返回了我的 graphCapsule 对象,并且它与 nullptr 不同。
然后我创建我的method1 函数如下:
static PyObject *method1(PyObject *self, PyObject *args) {
// Capsule with the pointer to `Graph` object
PyObject* graphCapsule_;
// Default parameters of the method1 function
size_t k = 300;
bool post_processing = true;
if (!PyArg_ParseTuple(args, "O|Ip", &graphCapsule_, &k, &post_processing)) {
return nullptr;
}
// Get the pointer to `Graph` object
Graph<float>* graph = reinterpret_cast<Graph<float>* >(PyCapsule_GetPointer(graphCapsule_, "graphptr"));
// Call method1
component<float> ctov = graph->method1(k, post_processing);
// Convert component<float> to a Python dict (bad because we need to copy?)
PyObject* result = parse_component<float>(ctov);
return result;
}
当我编译所有内容时,我将拥有一个 vgraph.so 库,我将使用以下方法从 Python 调用它:
import vgraph
import numpy as np
import scipy.misc
class Vgraph():
def __init__(self, img, similarity, d):
self.graphCapsule = vgraph.construct(img, similarity, d)
def method1(self, k=150, post_processing=True):
vgraph.method1(self.graphCapsule, k, post_processing)
if __name__ == "__main__":
img = scipy.misc.imread("pic.jpg")
img = scipy.misc.imresize(img, (512, 512)) / 255
g = Vgraph(lab_img, "1976", d=1)
cc = g.method1(k=150, post_processing=False)
这个想法是我保存vgraph.construct返回的PyObject pointer。然后我调用method1 传递PyObject pointer int k = 150 和bool postprocessing。
这就是为什么在 *method1 的 C++ 实现中,我使用:
!PyArg_ParseTuple(args, "O|Ip", &graphCapsule_, &k, &post_processing)解析这3个对象。
问题是,尽管在调试时,我恢复了 k=150 和 post_processing=False,它们来自我从 Python 调用 C++ 的方式......我也得到了 0X0 ,也就是说变量graphCapsule_中的一个nullptr...
所以很明显其余的代码不能工作......
我认为PyObject * 是指向我的Graph<float> * 类型的图 的指针,所以,我期待ParseTuple 恢复我的PyObject * 指针,然后我可以在PyCapsule_GetPointer 中使用它检索我的对象。
我怎样才能使我的代码工作?我是否需要定义自己的 PyObject 以便 ParseTuple 理解它?有没有更简单的方法?
非常感谢!
注意:如果我中断了我的 python 代码,我可以看到我的图形 g 包含一个 PyObject 及其指向的地址和对象的名称(这里是 @987654356 @) 所以我期待我的代码能够工作......
注意2:如果我需要创建自己的newtype,我看过这个stackoverflow帖子:How to wrap a C++ object using pure Python Extension API (python3)?,但我认为由于我的类的复杂对象,这将是相当困难的?
【问题讨论】:
标签: python c++ class binding wrapper