Recently there was a thread about function composition in Python (and this was probably not the first). The fast way to create a (anonymous) composite function
f1 o f2 o ... o fnin Python is via
lambda x: f1(f2(...fn(x)...))
,
but according to some this is neither the most compact nor the most
readable. I define a compose
function such that the above can
be written
compose(f1, f2, ...., fn)
,
the resulting funtion being as fast as the lambda version (or maybe
faster?). The file compose.py contains
the function definition (download here). The
function getcomposer
is a helper function (which in most
cases will amount to a dictionary lookup).
This is how it works
>>> def succ(x): return x+1 ... >>> def double(x): return 2*x ... >>> def square(x): return x**2 ... >>> def succ(x): return x+1 ... >>> f1 = compose(double, square, succ, float) >>> f1(8)162.0>>>
Here is a benchmark for speed against the traditional
lambda
method for composing functions. It shows that
using compose
incurs no speed penalty.
>>> f2 = lambda x: double(square(succ(float(x)))) >>> >>> def benchmark(f, n=1000000): ... from time import time ... t0 = time() ... for x in xrange(n): f(x) ... t1 = time() ... return t1-t0 ... >>> print 'compose', benchmark(f1)compose 1.83817005157>>> print 'lambda ', benchmark(f2)lambda 1.99733304977>>>
Looking at the bytecode for f1
and f2
:
>>> import dis >>> dis.dis(f1)1 0 LOAD_DEREF 0 (f0) 3 LOAD_DEREF 3 (f1) 6 LOAD_DEREF 1 (f2) 9 LOAD_DEREF 2 (f3) 12 LOAD_FAST 0 (x) 15 CALL_FUNCTION 1 18 CALL_FUNCTION 1 21 CALL_FUNCTION 1 24 CALL_FUNCTION 1 27 RETURN_VALUE>>> dis.dis(f2)23 0 LOAD_GLOBAL 0 (double) 3 LOAD_GLOBAL 1 (square) 6 LOAD_GLOBAL 2 (succ) 9 LOAD_GLOBAL 3 (float) 12 LOAD_FAST 0 (x) 15 CALL_FUNCTION 1 18 CALL_FUNCTION 1 21 CALL_FUNCTION 1 24 CALL_FUNCTION 1 27 RETURN_VALUE>>>
f1
and f2
are almost exaclty the same but
array lookups (LOAD_DEREFs) in f1
replace dictionary
lookups (LOAD_GLOBALs) in f2
. A C version of
compose
could easily be written that doesn't the use of a
python lambda-function (as created by getcomposer
).