python - Sort a list of cdef objects by attribute in a cdef class -
i sort list inner
of class. list contains objects of cdef class called edge
. edge class contains member variable called savings
. sort list variable.
cdef class alist: def __init__(self): self.inner = [] cdef list inner cdef void append(self, edge a): self.inner.append(a) cdef void pop(self, int a): self.inner.pop(a) cdef void insert(self,int pos,edge a): self.inner.insert(pos,a) cdef int index(self,edge a): if in self.inner: return self.inner.index(a) return -1 cdef edge get(self, int i): return <edge> self.inner[i] cdef void sort(self): self.inner.sort(key = lambda c : c.savings) #self.inner.sort() def __len__(self): return len(self.inner) def __richcmp__(edge self, edge other,int op): if op == 0: if self.inner.savings < other.inner.savings: return true return false
in order that, created method sort
inside class, when execute it, obtain following error message:
exception attributeerror: "'fib.edge' object has no attribute 'savings'" in 'fib.alist.sort' ignored
what going wrong here
the python- lambda function can not access cdef attribute of edge
. if try directly access attribute of cdef class python error unless add either readonly
(for read access) or public
(for read , write access) attribute definition.
take edge class example:
cdef class edge: cdef readonly int savings # visible python cdef int foo # not visible python def __init__(self, int s): self.savings = s self.foo = 42 def __repr__(self): """friendly representation can see how edges sort.""" return "edge: {}".format(self.savings)
if compile (assume resides in file sortlist.pyx
) , import in python shell:
in [1]: import sortlist sl in [2]: e = sl.edge(42) in [3]: e.savings out[3]: 23 in [4]: e.foo --------------------------------------------------------------------------- attributeerror traceback (most recent call last) <ipython-input-4-3ebfe6d526e9> in <module>() ----> 1 e.foo attributeerror: 'sortlist.edge' object has no attribute 'foo'
if access readonly
attribute saving
works, cython attribute foo
throws error getting. not attribute not there, python can not see it.
working example
basically fix is: add readonly
declaration of savings in edge class. edge class above works following code.
cdef class alist: cdef list inner def __init__(self): self.inner = [] cdef void append(self, edge a): self.inner.append(a) cdef void sort(self): self.inner.sort(key = lambda c : c.savings) def test_sorting(): # create edges saving between 0 9 edges = [edge(i) in range(10)] # create intersting instance sorting shuffle(edges) al = alist() # fill inner list e in edges: al.append(e) print("finished alist:", al.inner) al.sort() print("sorted alist:", al.inner)
why might not need richcmp (where now)
when define __richcmp__
method in alist
can used compare 2 alist
objects. hence typing edge not useful here.
what define richcmp
method edge
, meaning method can used compare 2 edge
objects. advise implement all comparisons then. because might show weird behavior like
in [1]: e1 = edge(42) in [2]: e2 = edge(42) in [3]: e3 = edge(23) in [4]: e1 < e2 out[4]: false # how supposed in [5]: e1 == e2 out[5]: false # should true in [6]: e1 < e3 out[6]: true # how supposed in [6]: e1 <= e3 out[6]: false # should true
because comparisons not op=0
<
operation (also available cpython.object.py_lt
) default false. if want use this, go way.
more details on rich comparison can found here.