cython c++ 类继承不能实例化派生类
Posted
技术标签:
【中文标题】cython c++ 类继承不能实例化派生类【英文标题】:cython c++ class inheritance cannot instantiate a derived class 【发布时间】:2017-09-21 11:38:00 【问题描述】:我正在尝试用 Cython 包装一些 c++ 类。对于基类,一切正常。对于具有相同参数(在其构造函数中)与其各自基类的参数的派生类,也会发生同样的情况。但是,我无法实例化具有与其基类不同数量的参数(在其构造函数中)的派生类。
例如:
h 文件// CYH.h file
#ifndef __CYH_H__
#define __CYH_H__
#include <iostream>
class Base
public:
Base(double);
~Base();
double A;
double getA();
void setA(double);
;
class Derived : public Base
public:
Derived(double, double);
~Derived();
double B;
;
#endif
cpp 文件
// cyh.cpp file
#include <iostream>
#include <string>
#include "include/cyh.h"
Base::Base(double a)
A = a;
Base::~Base()
double Base::getA()
return A;
void Base::setA(double a_)
A = a_;
Derived::Derived(double a, double b) : Base(a)
B = b;
Derived::~Derived()
和 pyx 文件
cdef extern from "include/cyh.h":
cdef cppclass Base:
Base(double)
double getA()
void setA(double)
cdef cppclass Derived(Base):
Derived(double, double)
cdef class cyBase:
cdef Base *thisptr
def __cinit__(self, double a):
if type(self) is cyBase:
self.thisptr = new Base(a)
def __dealloc__(self):
if type(self) is cyBase:
del self.thisptr
@property
def A(self):
return self.thisptr.getA()
@A.setter
def A(self, value):
self.thisptr.setA(value)
cdef class cyDerived(cyBase):
cdef Derived *thisptrDerived
def __cinit__(self, double a, double b):
if type(self) is cyDerived:
self.thisptrDerived = self.thisptr = new Derived(a, b)
def __dealloc__(self):
if type(self) is cyDerived:
del self.thisptrDerived
它编译并且 pyd 构建成功。但是,当我尝试使用以下方法实例化 cyDerived 类时:
mycyObj = cyDerived(a=2., b=3.)
我收到以下错误:
__cinit__() got an unexpected keyword argument 'b'
蚂蚁知道我做错了什么?
非常感谢!
【问题讨论】:
这可能解决了你的问题:***.com/a/33091422/5769463 非常感谢您的评论和链接。我在groups.google.com/forum/#!topic/cython-users/N-jXUWLzd6o 指出的基类中使用了 *args 和 **kwards。但是,据说这可能会带来效率损失(我不明白为什么)。我想知道是否有更有效的方法。 一种选择是在基类构造函数中“预期”b
并在那里忽略它 - 但这会使代码不那么干净,所以我只会这样做***.com/a/13811280/5769463
我使用了 *args 和 **kwards。为什么这会影响性能?在哪里?在类实例化期间?
【参考方案1】:
其实这是一个很有趣的问题:使用*args
、**kwargs
的性能损失是多少,哪些场景需要考虑?
从理论上看,如果使用*args
,**kwargs
的方案,cyBase
类的构造函数(每次调用)还有以下额外的工作需要做:
-
获取指向
args
和kwargs
的指针并在垃圾收集器中注册这些引用,
查字典(一些开销),
当不再需要时,取消注册垃圾收集器中的引用,以便可以销毁此字典。
我的假设是,注册/注销可能是个问题,因为参考更新需要获取 GIL。最好不要经常这样做。
没有*args
的解决方案似乎无需更改引用计数器即可工作。
到目前为止我的理论,但我希望看到一些实证结果,这可能会令人惊讶,所以如果你有一些数据,请与我们分享。
重现我的结果的代码:
#no_kwargs.pyx:
cdef class cyBase:
def __cinit__(self, double a):
pass
cdef class cyDerived:
def __cinit__(self, double a):
self.a=a
#kwargs.pyx
cdef class cyBase:
def __cinit__(self, *argv, **kwargs):
pass
cdef class cyDerived:
def __cinit__(self, double a):
self.a=a
运行它
python -m cython XXX.pyx
并比较生成的 c 文件。差异基本上在 cyBase::__cinit__()
发生整个引用计数的地方:
static int __pyx_pw_4test_6cyBase_1__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds)
...
__Pyx_INCREF(__pyx_args); // <----------- HERE
__pyx_v_argv = __pyx_args;
...
/* function exit code */
__Pyx_XDECREF(__pyx_v_argv); // <----------- HERE
...
return __pyx_r;
kwargs
这里好像没用过。
【讨论】:
您好,非常感谢您的宝贵回答。实际上,在 cyDerived 类中,我存储了大量数据,然后将这些数据传递给数值求解器进行计算。但是,对于我的每个模拟,我最多需要大约 100 个此类实例。我用这两种方法做了一个基准测试,在一个循环中,我用这两种方法实例化了 1000 个类,并且花费了完全相同的时间。但是,正如您所建议的,我现在将使用不带 *args 的解决方案。以上是关于cython c++ 类继承不能实例化派生类的主要内容,如果未能解决你的问题,请参考以下文章