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).