python - 使用 Cython 访问包装对象的成员变量
问题描述
我正在使用我以前从未使用过的 Cython 将现有的 C/C++ 库连接到 Python。它进展顺利,除了以下场景,我使用模型示例进行描述:
假设我有一个类“Person”,它有一个成员变量,一个名为“id”的int,另一个类“Group”有一个成员变量,一个名为“leader”的Person。类 Person 和 Group 还具有用于获取和设置其各自成员变量的成员函数,如下面的代码所示。我想要做的是:给定一个有领导的组,我希望能够访问组领导的 id(读取和修改)。例如,我希望下面的 Python 代码
from pyperson import PyPerson
from pygroup import PyGroup
nancy = PyPerson(7) # Create new person with id=7
my_group = PyGroup(nancy) # Create new group with leader=nancy
my.group.get_leader().set_id(12)
print(nancy.get_id())
输出数字 12。
我知道这个问题之前已经在这个网站上描述过(参见帖子底部的链接),但由于缺乏 C++ 能力,我无法成功地将那里给出的答案转移到我的虚拟示例中。
我写的代码如下:
人.h
#ifndef PERSON_H
#define PERSON_H
class Person {
private:
int my_id;
public:
Person();
Person(int id);
int getId();
void setId(int id);
};
#endif
个人.cpp
#include "person.h"
// Default constructor
Person::Person() {}
// Overloaded constructor
Person::Person(int id) {
this->setId(id);
}
// Get and set id
int Person::getId() {
return this->my_id;
}
void Person::setId(int id) {
this->my_id = id;
}
cperson.pxd
cdef extern from "person.cpp":
pass
cdef extern from "person.h":
cdef cppclass Person:
Person() except +
Person(int) except +
int getId();
void setId(int);
pyperson.pyx
# distutils: language = c++
from cperson cimport Person
cdef class PyPerson:
cdef Person c_person
def __cinit__(self, int id):
self.c_person = Person(id)
def get_id(self):
return self.c_person.getId()
def set_id(self, int id):
self.c_person.setId(id)
组.h
#ifndef GROUP_H
#define GROUP_H
#include "person.h"
class Group {
private:
Person* my_leader;
public:
Group();
Group(Person& leader);
Person* getLeader();
void setLeader(Person& leader);
};
#endif
组.cpp
#include "group.h"
#include "person.h"
// Default constructor
Group::Group() {}
// Overloaded constructor
Group::Group(Person& leader) {
this->setLeader(leader);
}
// Get and set leader
Person* Group::getLeader() {
return this->my_leader;
}
void Group::setLeader(Person& leader) {
this->my_leader = &leader;
}
cgroup.pxd
from cperson cimport Person
cdef extern from "group.cpp":
pass
cdef extern from "group.h":
cdef cppclass Group:
Group() except +
Group(Person&) except +
Person* getLeader();
void setLeader(Person&);
pygroup.pyx
# distutils: language = c++
from cperson cimport Person
from cgroup cimport Group
from pyperson import PyPerson
cdef class PyGroup:
cdef Group c_group
def __cinit__(self, Person& leader):
self.c_group = Group(leader)
def get_leader(self):
cdef PyPerson leader = PyPerson(0)
leader.c_person = self.c_group.getLeader()
return leader
def set_leader(self, Person& leader):
self.c_group.setLeader(leader)
安装程序.py
from setuptools import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize(["pyperson.pyx", "pygroup.pyx"]))
在包含上述代码的文件夹中执行命令“python3 setup.py build_ext --inplace”会产生以下错误消息:
Error compiling Cython file:
------------------------------------------------------------
...
def __cinit__(self, Person& leader):
self.c_group = Group(leader)
def get_leader(self):
cdef PyPerson leader = PyPerson(0)
^
------------------------------------------------------------
pygroup.pyx:14:13: 'PyPerson' is not a type identifier
Error compiling Cython file:
------------------------------------------------------------
...
def __cinit__(self, Person& leader):
self.c_group = Group(leader)
def get_leader(self):
cdef PyPerson leader = PyPerson(0)
leader.c_person = self.c_group.getLeader()
^
------------------------------------------------------------
pygroup.pyx:15:48: Cannot convert 'Person *' to Python object
Traceback (most recent call last):
File "setup.py", line 5, in <module>
setup(ext_modules=cythonize(["pyperson.pyx", "pygroup.pyx"]))
File "/home/sindre/.local/lib/python3.8/site-packages/Cython/Build/Dependencies.py", line 1102, in cythonize
cythonize_one(*args)
File "/home/sindre/.local/lib/python3.8/site-packages/Cython/Build/Dependencies.py", line 1225, in cythonize_one
raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: pygroup.pyx
在解决这些错误和实现既定目标方面的任何帮助将不胜感激。
正如所承诺的,这里是我检查过的 stackoverflow-posts 的链接: 如何从另一个包装的对象返回 Cython 中的包装 C++ 对象? 如何在 Cython 中返回 C++ 包装对象 Cython - 将 C++ 函数返回的 C++(向量和非向量)对象 暴露给 Python 如何在不复制对象的情况下将返回 C++ 对象的函数暴露给 Python?
编辑:
根据 DavidW 的建议,我将 pygroup.pyx 中的第 5 行更改为阅读
from pyperson cimport PyPerson
但这并没有解决我的问题。相反,我读到了一个额外的编译错误
pygroup.pyx:5:0: 'pyperson.pxd' not found
考虑到 PyPerson 是在 pyperson.pyx 中定义的,而不是在 pyperson.pxd 中,我认为这个错误是有道理的。
我进行了更多实验,看看我是否可以自己解决错误,并最终在 pygroup.pyx 中得到以下代码(我没有更改上述任何其他文件):
# distutils: language = c++
from cperson cimport Person
from cgroup cimport Group
from pyperson import PyPerson
cdef class PyGroup:
cdef Group c_group
def __cinit__(self, Person& leader):
self.c_group = Group(leader)
def get_leader(self):
leader = PyPerson(0)
leader.c_person = self.c_group.getLeader()
return leader
def set_leader(self, Person& leader):
self.c_group.setLeader(leader)
使用上面的代码,我只收到以下错误:
Error compiling Cython file:
------------------------------------------------------------
...
def __cinit__(self, Person& leader):
self.c_group = Group(leader)
def get_leader(self):
leader = PyPerson(0)
leader.c_person = self.c_group.getLeader()
^
------------------------------------------------------------
pygroup.pyx:15:48: Cannot convert 'Person *' to Python object
这让我很困惑,因为leader.c_person
考虑到它c_person
是使用cdef
. 我也不明白如何将 Person 的实例转换为 Python 可访问的东西。
解决方案
推荐阅读
- ruby-on-rails - 有人可以解释一下each_with_index,reduce,select与OR(||)一起在代码中链接的用法吗?
- r - 删除字符串中重复两次以上的字符
- swift - iOS 9 中的 AVAudioSession.sharedInstance().setCategory
- android-intent - 如何防止在 Kotlin Android Intent 期间执行新活动中的方法
- r - 如何通过 R 脚本直接将 xtable 导出为 PDF?
- python - 多个布尔运算,例如 (l 和 r) ==None:
- javascript - 在收到握手响应 sipml5 之前连接关闭失败
- c++ - 如何在 %appdata% 中创建文件夹,在其中创建 .bat 文件,然后执行?
- angular - 基于所选计数的 Ng Bootstrap 评级星颜色
- javascript - jQuery DataTables:如果字符串存在于?