Похоже, вы пытаетесь использовать свойство так, как оно не было предназначено для работы.Можно использовать свойства в вашем сценарии, но с точки зрения производительности это может быть нецелесообразно, так как этот ответ будет показан ниже.
В чистом 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 преобразовал массив C int
в список (что накладные расходы!), И изменение этой копии массива Addr
не изменяет исходный массив приall.
Вам необходимо вернуть (типизированное) представление памяти массива _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 раз быстрее!