A class to allow programmers to curry functions, so that arguments can be supplied one at a time instead of all at once. E.g., if F = Curry(lambda a,b: a+b), then F(1,2) == F(1)(2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | CO_VARARGS = 0x0004
CO_VARKEYWORDS = 0x0008
class Curry:
def __init__(self, f):
self.hasv = f.func_code.co_flags & CO_VARARGS
self.hask = f.func_code.co_flags & CO_VARKEYWORDS
self.defaults = f.func_defaults or ()
self.defnum = len(self.defaults)
self.f = f
self.argnum = f.func_code.co_argcount
self._reset()
def __call__(self, *a, **k):
if k and not self.hask:
raise TypeError, "%s got unexpected keyword argument '%s'" %\
(self.f.__name__, k.popitem()[0])
kargs = self.kargs
args = self.args
kargs.update(k)
totlen = len(args) + len(a)
if totlen > self.argnum:
if not self.hasv:
raise TypeError, "%s takes exactly %d argument%c (%d given)" % (self.f.__name__, self.argnum, ['s',''][self.argnum==1], totlen)
args += a
self._reset()
return self.f(*args, **kargs)
if totlen >= self.argnum - self.defnum:
num_defaults = totlen - defnum
args += a + self.defaults[defnum-num_defaults:]
self._reset()
return self.f(*args, **kargs)
self.args += a
return self
def _reset(self):
self.args, self.kargs = (), {}
|
Curried functions can be useful when creating callbacks if you want to use a pre-existent function, or a function which needs access to more information than will be provided by the caller of the callback. They can be used as an alternative to lambdas, in that you won't need to provide a (possibly long) list of default arguments, reducing readability; however, lambdas have the advantage that the arguments can be given in any order. That is, lambda a, b, c=something: function(c, a, b) could be replaced by function = Curry(function) function(something) but lambda a,b,c=something: function(a,b,c) could not.
One drawback to this approach to currying is that methods are generally slower than ordinary functions. I did implement the same algorithm using nested functions and (prior to nested scopes) docstring abuse, but that was actually slower, though not by much.
A few bugs and a test. I had to add "self." to these two lines to get the code to work.
<p> num_defaults = totlen - self.defnum
Here is a test: