pythonic way of composing itemgetter and attrgetter?
Question:
I’ve got some code I’m porting to Cython which had a line like
my_list.sort(key=lambda x: x.attr[item])
Is there a nice pythonic way of avoiding the closure with some combination of itemgetter and attrgetter?
Answers:
The key is to use the package functional
:
from functional import compose
from operator import attrgetter, itemgetter
my_list.sort(key=compose(itemgetter(item), attrgetter('attr')))
The accepted self answer feels like a mistake/de-optimization to me.
My guess is that the unstated problem is that closures aren’t supported inside cpdef
functions. However closures are supported inside both cdef
and def
functions.
My view is that there’s rarely a reason to use cpdef
functions – they have all the disadvantages of def
functions and all the disadvantages of cdef
functions (plus a few more unique disadvantages, like no closures) so I usually treat them as the worst of all worlds. Ideally you’d just decide if something should be a Cython/C interface (cdef
) or a Python interface def
and use that. Also remember that the type of function makes little to no difference to how the code inside it is compiled, so a find/replace of def
for cpdef
really is redundant when porting to Cython.
Therefore for this case I would keep the closure as written in the original Python code and pick one of:
- Just keep it as a
def
function.
- If you really need to call it from both Python and need the speed of a
cdef
call when calling it from Cython then write a cdef
function and a really small def
function wrapper for the cdef
function.
The code in the accepted answer is de-optimizing an attribute lookup and an index into at least(?) 3 Python function calls, an attribute lookup, and an index.
I’ve got some code I’m porting to Cython which had a line like
my_list.sort(key=lambda x: x.attr[item])
Is there a nice pythonic way of avoiding the closure with some combination of itemgetter and attrgetter?
The key is to use the package functional
:
from functional import compose
from operator import attrgetter, itemgetter
my_list.sort(key=compose(itemgetter(item), attrgetter('attr')))
The accepted self answer feels like a mistake/de-optimization to me.
My guess is that the unstated problem is that closures aren’t supported inside cpdef
functions. However closures are supported inside both cdef
and def
functions.
My view is that there’s rarely a reason to use cpdef
functions – they have all the disadvantages of def
functions and all the disadvantages of cdef
functions (plus a few more unique disadvantages, like no closures) so I usually treat them as the worst of all worlds. Ideally you’d just decide if something should be a Cython/C interface (cdef
) or a Python interface def
and use that. Also remember that the type of function makes little to no difference to how the code inside it is compiled, so a find/replace of def
for cpdef
really is redundant when porting to Cython.
Therefore for this case I would keep the closure as written in the original Python code and pick one of:
- Just keep it as a
def
function. - If you really need to call it from both Python and need the speed of a
cdef
call when calling it from Cython then write acdef
function and a really smalldef
function wrapper for thecdef
function.
The code in the accepted answer is de-optimizing an attribute lookup and an index into at least(?) 3 Python function calls, an attribute lookup, and an index.