Welcome, guest | Sign In | My Account | Store | Cart

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)

Python, 35 lines
 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.

1 comment

s g 17 years ago  # | flag

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

      args += a + self.defaults[self.defnum-num_defaults:]

Here is a test:

def myfun(a,b):
    print a,b

myfun = Curry(myfun)
myfun(1)
myfun(2)
myfun(3)(4)
myfun(5,6)  # the usual way
Created by Ben Wolfson on Mon, 9 Apr 2001 (PSF)
Python recipes (4591)
Ben Wolfson's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks