I Might Be Wrong

thunkurry: a Python decorator for partial application

Posted in Python by Leif Ryge on September 30, 2010

Here is a decorator which allows you to curry named and/or positional arguments as many times as you like. The underlying function is finally called when the decorated function is called as a thunk (meaning, without any arguments).

def thunkurry(f_, *t_, **d_):
    """Currying thunk decorator
    Example usage:
    >>> prod = lambda *a: reduce(operator.mul, a)
    >>> sum_ = lambda *a: sum(a)
    >>> thunkurry(prod)(2)(3)(4)() == prod(2,3,4) == 24
    True
    >>> thunkurry(lambda a,b,c:a+b+c)(c=1)(2)(c=3)(4)()
    9
    >>> a = thunkurry(sum_)
    >>> a = a(1)(2)(3); a()
    6
    >>> a = a(1)(2)(3); a()
    12
    """
    def fn(*T_, **D_):
        return f_(*t_, **d_) if not (T_ or D_) \
                else thunkurry(f_, *t_ + T_, **dict(d_.items() + D_.items()))
    fn.__doc__ = f_.__doc__
    return fn

I’ve mostly found this useful as a class decorator.

(Note: the Talk:Currying page on wikipedia has some discussion about the terms currying vs. partial application; I’m using the terms interchangeably here and am not sure if that is wrong.)

Tagged with: , , , ,

Memoization in one line

Posted in Python by Leif Ryge on August 27, 2008

The memoization decorator in the Python Decorator Library has served me well, but, after reading stupid lambda tricks, I started thinking about how it could be reduced to a single expression. This is the result:

memoize = lambda f: (lambda d={}: lambda *a: d.setdefault(a, a in d or f(*a)))()

Unlike the original, mine doesn’t catch the case of the memoized function being called with unhashable (mutable) arguments. It doesn’t seem like that is a likely scenario, though.

Here is another version which also works with functions taking named arguments:

memo_with_named_args = lambda f: (lambda d={}: lambda *a,**kw: \
    (lambda *k: d.setdefault(k, k in d or f(*a,**kw)))(tuple(kw.items()),a) )()