【问题标题】:Build an extension class by inheriting a python class in Cython通过在 Cython 中继承一个 python 类来构建一个扩展类
【发布时间】:2021-05-19 21:47:42
【问题描述】:

我想通过继承 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, ways, 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, ways, relations
    def __init__(self):
        DummyBase.__init__(self)
        osmium.SimpleHandler.__init__(self)
        self.nodes = []
        self.ways = []
        self.relations = []

    cdef node(self, n):
        pass

    cdef way(self, w):
        pass

    cdef relation(self, r):
        pass

错误信息:

Traceback (most recent call last):
  File "C:/Users/Administrator/Dropbox (ASU)/Work/CAVLite/OSM2GMNS/V2/cython_test/tets.py", line 7, 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'

【问题讨论】:

    标签: python cython subclass cythonize


    【解决方案1】:

    这里有很多选项:

    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 中重写东西会加快速度。

    【讨论】:

    • 基类osmium.SimpleHandler有node()、way()和relation()函数,我将在我的类CounterHandler中重写它们。我在这里缺少的是在我的类 CounterHandler 中定义这三个函数时应该使用 cdef。我希望我能在打电话给他们时加快速度。这就是为什么我需要定义一个 cdef 类的原因。尝试方法2和3后,导入构建的pyd文件时出错。我列出了我使用的代码和我在上面的问题中遇到的错误,以提供清晰的视图。
    • 基于那些错误选项 2 和 3 永远不会工作(见编辑)。如果您真的认为您需要cdef class,那么不幸的是,我认为没有好的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2014-11-06
    • 2016-02-28
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 2011-10-19
    • 1970-01-01
    相关资源
    最近更新 更多