通过在 Cython 中继承 python 类来构建扩展类

问题描述

我想通过在 Cython 中继承一个 python 类来构建一个子类。似乎我不能直接这样做,因为我在下面遇到错误。有什么解决办法吗?

代码(osmium是第三方python包,可以使用pip安装):

import osmium

cdef class CounterHandler(osmium.SimpleHandler):
    cdef list nodes,ways,relations
    def __init__(self):
        osmium.SimpleHandler.__init__(self)
        self.nodes = []
        self.ways = []
        self.relations = []

    def node(self,n):
        pass

    def way(self,w):
        pass

    def relation(self,r):
        pass

错误信息:

add.pyx:22:32: First base of 'CounterHandler' is not an extension type
Traceback (most recent call last):
  File "setup.py",line 11,in <module>
    ext_modules=cythonize("add.pyx"))
  File "C:\ProgramData\Miniconda3\envs\osmium\lib\site-packages\Cython\Build\Dependencies.py",line 1102,in cythonize
    cythonize_one(*args)
  File "C:\ProgramData\Miniconda3\envs\osmium\lib\site-packages\Cython\Build\Dependencies.py",line 1225,in cythonize_one
    raise CompileError(None,pyx_file)
Cython.Compiler.Errors.CompileError: add.pyx

我尝试了 DavidW 提供的解决方

解决方案 2 代码

import osmium

cdef class CounterHandlerBase:
    cdef list nodes,relations
    def __init__(self):
        self.nodes = []
        self.ways = []
        self.relations = []

    cdef node(self,n):
        pass

    cdef way(self,w):
        pass

    cdef relation(self,r):
        pass

class CounterHandler(CounterHandlerBase,osmium.SimpleHandler):    # osmium.SimpleHandler
    def __init__(self):
        CounterHandlerBase.__init__(self)
        osmium.SimpleHandler.__init__(self)

错误信息:

Traceback (most recent call last):
  File "C:/Users/Administrator/DropBox (ASU)/Work/CAVLite/OSM2GMNS/V2/cython_test/tets.py",line 7,in <module>
    import solution2 as solution
  File "solution2.pyx",line 28,in init solution2
    class CounterHandler(CounterHandlerBase,osmium.SimpleHandler):    # osmium.SimpleHandler
TypeError: multiple bases have instance lay-out conflict

解决方案 3 代码

import osmium

cdef class DummyBase:
    def __init__(self):
        pass

cdef class CounterHandler(DummyBase,osmium.SimpleHandler):    # osmium.SimpleHandler
    cdef list nodes,relations
    def __init__(self):
        DummyBase.__init__(self)
        osmium.SimpleHandler.__init__(self)
        self.nodes = []
        self.ways = []
        self.relations = []

    cdef node(self,r):
        pass

错误信息:

Traceback (most recent call last):
  File "C:/Users/Administrator/DropBox (ASU)/Work/CAVLite/OSM2GMNS/V2/cython_test/tets.py",in <module>
    import solution3 as solution
  File "solution3.pyx",line 16,in init solution3
    cdef class CounterHandler(DummyBase,osmium.SimpleHandler):    # osmium.SimpleHandler
TypeError: best base 'osmium._osmium.SimpleHandler' must be equal to first base 'solution3.DummyBase'

解决方法

这里有很多选择:

  1. 你真的需要它成为 cdef class 吗?您对此是否有真正的原因(除了普遍的、未经测试的“cdef classes 更快”的信念之外)?也许您可以改用常规课程?您不希望使用任何无法在 Python 中表示的属性(例如 C 指针)。请记住,Cython 仍然编译常规类的 def 函数,因此可能没有您想象的速度差异。

  2. 将其拆分为需要是 cdef class 的位和不需要的位(这仅在与 osmium.SimpleHandler 的交互位于不需要的位时才有效) :

    cdef class CounterHandlerBase:
        # code goes here
    
    class CounterHandler(CounterHandlerBase,osmium.SimpleHandler):
        # more code goes here
    
    
  3. 限制是 first 基数必须是 cdef class(这实际上是 Python 内置的一个相当强的限制)。第二个/后续基础可以是常规类。因此,您可以创建一个“虚拟”cdef 基类来填补该角色:

    cdef class DummyBase:
        pass
    
    cdef class CounterHandler(DummyBase,osmium.SimpleHandler):
        # code goes here...
    

编辑:根据您报告的错误,osmium.SimpleHandler 似乎已经是用 C/C++ 编写的扩展类型。不幸的是,这意味着不可能在 cdef class 中继承它,因为 Python 中内置的对象布局的限制(将其定义为 "external cdef class" 可能会起作用,但它看起来是从 by pybind11 生成的,这使得计算底层结构变得非常困难)。

因此,在这种情况下,选项 2 和 3 永远不会起作用。由于它已经是用 C++ 编写的,我怀疑在 Cython 中重新编写东西是否会加快速度。