我知道这是重复的 Writing Cython extension: how to access a C struct internal data from Python?
但是我没有找到任何可以处理C结构的资源,
ctypedef struct IoRegAccess:
int Addr[26]
int Data[26]
int AddrLen
int DataLen
使用__getitem__
/ __setitem__
方法,我们可以访问此struct数组,但我有兴趣在特定类中使用@property进行操作
cdef class PyIoRegAccess:
cdef IoRegAccess RegContainer
#ctor of this class
def __cinit__(self):
memset(self.RegContainer.uiAddr, 0,sizeof(uint32_t)*26)
memset(self.RegContainer.uiData, 0,sizeof(uint32_t)*26)
@property
def uiAddr(self,key):
return self.RegContainer.uiAddr[key]
@uiAddr.setter
def uiAddr(self, key, value):
self.RegContainer.uiAddr[key] = value
现在我遇到两个错误,
特殊方法__get__
的参数数量错误(已声明2个,预期1个)
特殊方法__set__
的参数数量错误(已声明3个,预期2个)
请对此提供建议
答案 0 :(得分:2)
您似乎正在尝试以某种不希望使用的方式来使用属性。可以在您的方案中使用属性,但是从性能的角度来看,这可能并不明智,因为此答案将在下面进一步显示。
在纯Python中,您可以使用(您肯定已经知道)以下属性来访问列表中的元素:
def class A:
def __init__(self):
self._lst=[1,2,3,4]
@property
def lst(self):
return self._lst
即属性不用于访问列表的元素,而是用于列表本身。
现在
a=A()
a.lst # accesses list via property
a.lst[0] = 10 # accesses list via property + __getitem__ of the list
# no property-setter needed, we don't set lst-property,
# just an element of the list
a.lst[0] # returns 10
将同一个想法天真转换为Cython(您的示例有些简化):
%%cython
from libc.string cimport memset
cdef class CyA:
cdef int _Addr[26]
def __cinit__(self):
memset(self._Addr, 0,sizeof(int)*26)
@property
def Addr(self):
return self._Addr
但是,这不符合人们的预期:
a=CyA()
a.Addr[0] = 10
a.Addr[0] # returns 0!
问题在于,Cython在后台将int
C数组转换为列表(这是一个开销!),并且更改此Addr
数组的副本不会更改原始数组数组。
您需要在属性中返回数组_Addr
的(类型化)内存视图:
%%cython
....
@property
def Addr(self):
cdef int[:] memview = self._Addr
return memview
可按预期工作:
a=CyA()
a.Addr[0] = 10
a.Addr[0] # returns 10, as expected!
您可能会担心只为一个访问创建一个内存视图的开销(这是正确的),在这种情况下,可以缓存创建的内存视图并反复使用:
%%cython
cdef class CyA:
cdef int _Addr[26]
cdef int[:] memview_cache
def __cinit__(self):
memset(self._Addr, 1204,sizeof(int)*26)
self.memview_cache = None
...
@property
def Addr_fast(self):
if self.memview_cache is None:
self.memview_cache = self._Addr
return self.memview_cache
导致因子3
加速:
a=CyA()
%timeit a.Addr # 1.05 µs ± 36.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit a.Addr_fast # 328 ns ± 13.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
然而,与通过__setitem__
通过元素进行非花哨且直接的设置相比,这仍然是过多的开销:
%%cython
cdef class CyA:
cdef int _Addr[26]
...
def __setitem__(self, index, int value):
self._Addr[index] = value
这导致
a=CyA()
%timeit a.Addr_fast[0] = 10 # 483 ns ± 22.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit a[0] = 10 # 32.5 ns ± 0.669 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
快10倍!