如何将Cython扩展类型作为参数传递给Cython函数/方法?

时间:2018-12-22 23:49:54

标签: cython

我正在使用Cython编写程序。我有用Cython编写的cdef类(扩展类型)。我的扩展类型中的某些方法使用其他扩展类型(对象)的实例作为参数。我还希望能够将指针传递给我的扩展类型,并将这些指针存储在向量中。尝试编译时,出现错误,提示无法将这些扩展转换为Python对象。我可以将基本类型的实例(例如3(int的实例))作为参数传递,但我自己定义的都没有。

以下内容说明了我想要在C ++中将对象作为参数传递的内容:

class Bar {
    // ...
};

class Foo{
        void ffoo(Bar arg){
        // ...
    }
};

以下内容在cython中进行了说明,但不正确:

cdef class Bar:
    # ...

cdef class Foo:
    cdef void ffoo(self, Bar arg):
        # ...

以下内容说明了我想要在C ++中将对象作为参数传递的内容:

class Base {
    // ...
};

class Derived : public Base {
    // ...
};

class Foo{
        void ffoo(Base* arg){
        // ...
        std::vector<Base*> internal_vec;
    }
};

在问题的结尾,我尝试这样做。

基本上,我需要将类Bar的实例作为参数传递。 (请参见下面的首次尝试)

我还想存储一个指向基本类型的指针向量。

这可能吗?

我已经检查了Cython文档,博客以及有关StackOverflow的类似问题,但似乎无法使这个非常基本的功能正常工作。

下面是我的尝试。

第一次尝试(继承自对象,将对象作为参数类型传递)

setup.py

from setuptools import setup, Extension
from Cython.Build import cythonize

pkg_name = 'soquestion'

compile_args = []
bar = Extension(
        name=pkg_name + '.bar',
        sources=[
            pkg_name + '/bar.pyx',
        ],
        language='c++',
        extra_compile_args=compile_args,
    )

foo = Extension(
        name=pkg_name + '.foo',
        sources=[
            pkg_name + '/foo.pyx',
        ],
        language='c++',
        extra_compile_args=compile_args,
    )

setup(
    name=pkg_name,
    ext_modules=cythonize(bar,
                            annotate=True,
                            build_dir='build') \
                            + cythonize(foo,
                            annotate=True,
                            build_dir='build'),# \
    packages=[
        pkg_name
    ],
)

bar.pxd

cdef class Bar(object):
    cdef int i

bar.pyx

cdef class Bar(object):
    def __cinit__(self):
        pass
    def __init__(self):
        pass

Language Basics

  

名称对象还可以用于将某些内容明确声明为   Python对象。

foo.pxd

from soquestion.bar cimport Bar

cdef class Foo(object):
    cpdef void ffoo(self, object arg)

foo.pyx

from soquestion.bar cimport Bar

cdef class Foo(object):
    cpdef void ffoo(self, object arg):
        # obviously, cython would have to know that arg has member i
        arg.i = 3
        print(arg.i)

输出

>>> from soquestion.bar import Bar
>>> from soquestion.foo import Foo
>>> a = Bar
>>> b = Foo
>>> b.ffoo(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor 'ffoo' requires a 'soquestion.foo.Foo' object but received a 'type'

第二次尝试(存储指向基本类型的指针)

(我有点懒,但是派生类仍然应该能够保存指向基本类型的指针的向量。)

setup.py

from setuptools import setup, Extension
from Cython.Build import cythonize

pkg_name = 'soquestion'

compile_args = []
bar = Extension(
        name=pkg_name + '.bar',
        sources=[
            pkg_name + '/bar.pyx',
        ],
        language='c++',
        extra_compile_args=compile_args,
    )

foo = Extension(
        name=pkg_name + '.foo',
        sources=[
            pkg_name + '/foo.pyx',
        ],
        language='c++',
        extra_compile_args=compile_args,
    )

derived = Extension(
        name=pkg_name + '.derived',
        sources=[
            pkg_name + '/derived.pyx',
        ],
        language='c++',
        extra_compile_args=compile_args,
    )


setup(
    name=pkg_name,
    ext_modules=cythonize(bar,
                            annotate=True,
                            build_dir='build') \
                            + cythonize(foo,
                            annotate=True,
                            build_dir='build'), \
                            + cythonize(derived,
                            annotate=True,
                            build_dir='build') \

    packages=[
        pkg_name
    ],
)

bar.pxd

cdef class Bar(object):
    cdef int i

bar.pyx

cdef class Bar(object):
    def __cinit__(self):
        pass
    def __init__(self):
        pass

foo.pxd

cdef class Foo(object):
    cpdef void ffoo(self, object arg)

foo.pyx

cdef class Foo(object):
    cpdef void ffoo(self, object arg):
        # obviously, cython would have to know that arg has member i
        arg.i = 3
        print(arg.i)

derived.pxd

from libcpp.vector cimport vector
from soquestion.bar cimport Bar

cdef class Derived(Bar):
    cdef vector[Bar*] vec
    cdef void store_ptr(self, Bar* b)

derived.pyx

from libcpp.vector cimport vector
from soquestion.bar cimport Bar

cdef class Derived(Bar):
    def __cinit__(self):
        pass
    def __init__(self):
        pass

    cdef void store_ptr(self, Bar* b):
        self.vec.push_back(b)

输出

在出现Bar *的每一行上,我得到

soquestion/derived.pxd:5:20: Pointer base type cannot be a Python object

1 个答案:

答案 0 :(得分:0)

这基本上应该是您编写的方式。 Python(以及Cython)需要一个显式的self参数,这与C ++具有一个隐式的this参数是不同的。

cdef class Foo: 
    cdef void ffoo(self, Bar arg):
       pass

您无需键入self,因为Cython可以解决此问题。


就您的示例而言:无需继承object-cdef class仍然是Python对象。我认为这没有什么害处,但是可能会带来一些意想不到的后果。

在您的第一个示例中,它失败了,因为您传递了类型对象而不是Python实例。 这是您对Python的理解的一个基本错误,与Cython无关。。调用类型对象以创建实例:

>>> a = Bar()
>>> b = Foo()
>>> b.ffoo(a)

您的第二个示例正在尝试使用C ++容器存储cdef class实例的集合。这是可能的,但是它比您想象的要复杂得多。除其他事项外,您还需要非常小心地进行引用计数。如果Bar*Bar,则Cython不会识别cdef class。只需使用Python列表即可。