instancemethod provides a way to perform currying such that the curried function runs much faster than one produced by closure (the second-fastest way). [Currying is explained and covered in detail in other recipes]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # Consider this code in module a.py:
import new
def curry1(func, arg):
return new.instancemethod(func, arg, object)
def curry2(func, arg):
def curried(*args): return func(args, *args)
return curried
def f(a, b, c): return a, b, c
g1=curry1(f, 23)
g2=curry2(f, 23)
# g1 and g2 have the same behavior. But NOT the same performance...:
#[alex@lancelot ba]$ timeit.py -c -s'import a' 'a.g1(45,67)'
# 1000000 loops, best of 3: 1.37 usec per loop
#[alex@lancelot ba]$ timeit.py -c -s'import a' 'a.g2(45,67)'
# 100000 loops, best of 3: 2.4 usec per loop
# curry1-produced g1 is faster enough than curry2-produced g2 to make
# this worth keeping in mind for curried functions that must be run
# as part of a program's performance bottleneck.
|
Currying is an important technique to build callables on the fly and is well covered in other recipes. If the callables need to be used in a performance bottleneck, the specific advantage of currying via new.instancemethod is speed of the resulting callable: it avoids nesting an extra Python function call, which may be important. In the above little benchmark, for example, a microsecond per call was saved.
Caveat. Neat trick, but be careful - it won't work if arg is None.
Typo. In curry2:
should be
I think.
partial application. Very cool! I have been thinking about doing a C implementation of this partial application for a long time; this will hold me off for the time being :-) I still might, though, if I ever find a need for applying partially to keyword arguments or ordered arguments other than the first one.
It's a neat hack, I just hope python-dev doesn't decide to break it ;-)
an old bytecodehack is probably faster still. If you still have 1.5.2 lying around somewhere, you could look at bytecodehacks.xapply. It works by rewriting the bytecode of a function with 'LOAD_FAST n' for small n into an appropriate LOAD_CONST.
It was also by far the hairiest code I'd ever written at the time :-)
Binding multiple arguments. For binding a single argument, new.function has the same performance as new.instancemethod, and it can be used to bind arbitrarily many arguments with equal performance:
I might be dense, but ... I might be dense, but, using 2.3.2 on Win2K SP4+: