我有兴趣创建一个数据结构来保存传递给不同函数的数据/信息。我们目前的做法是如下代码:
# right now we use a C-struct to hold data that is passed
# around in a separate class 'A'
cdef struct Record:
double threshold
double improvement
cdef class A:
cpdef py_dostuff(self):
cdef Record record
record.threshold = new_threshold
record.improvement = new_improvement
cy_dostuff(&record)
cdef void cy_dostuff(self, Record record) nogil:
do_some_computation(record.threshold, record.improvement)
这使用了一个c风格的结构体,不幸的是它不支持继承,所以如果我们想用另一个使用该结构体的"子类"的类"B"创建子类"a",它就不起作用了。我使用类的尝试没有成功。理想情况下,我将能够在不牺牲性能的情况下执行以下操作。我的想法是,应该有可能用纯Cython扩展类型替换结构,因为我只使用c级的东西,但扩展类型将使我能够子类化Record
和A
。
# now, I would like to use a Cython extension type to hold data that is passed
# around in a separate class 'A'
cdef class Record:
cdef double threshold
cdef double improvement
cdef class A:
cpdef py_dostuff(self):
cdef Record record
record.threshold = new_threshold
record.improvement = new_improvement
cy_dostuff(&record)
cdef void cy_dostuff(self, Record record) nogil:
do_some_computation(record.threshold, record.improvement)
# The reason I would like to use a Cython extension type is that it can then support clean inheritance of the data structure
cdef class NewRecord(Record):
cdef double threshold
cdef double improvement
cdef int new_attribute
# E.g. a new subclass of 'A' would still work even if all we did was extend the logic to a "NewRecord"
cdef class B(A):
cpdef py_dostuff(self):
cdef NewRecord record
record.threshold = new_threshold
record.improvement = new_improvement
record.new_attribute = new_attribute
cy_dostuff(&record)
cdef void cy_dostuff(self, Record record) nogil:
do_some_computation(record.threshold, record.improvement, record.new_attribute)
我的问题是:
- 我如何用纯Python类(没有Python对象允许nogil操作)代替结构正确?
- 会有性能差异吗?
- 如果我不能,为什么不和什么是变通的传递结构,如数据结构周围?
我如何正确地替换纯Python类(没有Python对象允许nogil操作)来代替结构?
使用Cythoncdef class
代替结构体没有什么特别棘手的——你可以将它们传递给nogil
函数并访问它们的非object
cdef
属性,而不需要GIL:
cdef class A:
cdef double threshold
cdef double improvement
def example_func(self):
with nogil:
self.threshold = do_something(self)
cdef double do_something(A a) nogil:
return a.threshold
请考虑你是否真的需要在没有GIL的情况下工作(即你正在做多线程)。很多人认为"nogil==fast";并要求nogil解决方案,主要是出于货物崇拜的原因。
注意,不能取cdef class
的地址。在您的(非工作)示例中,cy_dostuff(&record)
将变为cy_dostuff(record)
。
会有性能差异吗?
可能不多。cdef class
本质上是一个结构体。在内部通过指针(而不是通过值)并在堆上分配,因此这可能会有一点不同。不过,Cython会为您处理这些细节。
如果我不能,为什么不和什么是变通的传递结构像数据结构周围?
你可以通过组合进行继承:
cdef struct Record:
double threshold
double improvement
cdef struct NewRecord:
Record base
int new_attribute
C标准明确允许在Record
和NewRecord
指针之间强制转换,以支持这种确切的使用。所以如果你有一个接受Record
指针的函数你可以使用f(<Record*>&my_new_record)