cython c ++类继承无法实例化派生类

时间:2017-09-21 11:38:00

标签: python c++ inheritance cython

我试图用Cython包装一些c ++类。对于Base Classes,一切正常。对于具有与其各自基类具有相同参数(在其构造函数中)的派生类,也会发生相同的情况。但是,我无法实例化一个派生类,该派生类与其基类具有不同数量的参数(在其构造函数中)。

例如:

  • h file
// 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 file
// 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'

Ant想法我做错了什么?

非常感谢提前!

1 个答案:

答案 0 :(得分:2)

实际上这是一个非常有趣的问题:使用*args**kwargs会对性能造成什么影响?应该考虑哪些情况?

从理论的角度来看,如果cyBase,{{*args**kwargs类的构造函数(每次调用它)都需要完成以下额外的工作。使用1}}解决方案:

  1. 获取指向argskwargs的指针,并在垃圾收集器中注册这些引用,
  2. 检查字典(一些开销),
  3. 当不再需要时,取消注册垃圾收集器中的引用,这样就可以销毁这个字典。
  4. 我的假设是,注册/取消注册可能是个问题,因为参考更新需要获取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似乎在这里未使用。