Currying decorator in python

前端 未结 9 1696
故里飘歌
故里飘歌 2020-12-03 03:11

I am trying to write a currying decorator in python, and I think I\'ve got the general idea down, but still got some cases that aren\'t working right...

def          


        
相关标签:
9条回答
  • 2020-12-03 03:47

    As it's cool to write currying decorators in python, I tried mine: 5 lines of code, readable, and tested curry function.

    def curry(func):
        def curried(*args, **kwargs):
            if len(args) + len(kwargs) >= func.__code__.co_argcount:
                return func(*args, **kwargs)
            return (lambda *args2, **kwargs2:
                    curried(*(args + args2), **dict(kwargs, **kwargs2)))
        return curried
    
    0 讨论(0)
  • 2020-12-03 03:48

    Here is my version of curry that doesn't use partial, and makes all the functions accept exactly one parameter:

    def curry(func):
    """Truly curry a function of any number of parameters
    returns a function with exactly one parameter
    When this new function is called, it will usually create
    and return another function that accepts an additional parameter,
    unless the original function actually obtained all it needed
    at which point it just calls the function and returns its result
    """ 
    def curried(*args):
        """
        either calls a function with all its arguments,
        or returns another functiont that obtains another argument
        """
        if len(args) == func.__code__.co_argcount:
            ans = func(*args)
            return ans
        else:
            return lambda x: curried(*(args+(x,)))
    
    return curried
    
    0 讨论(0)
  • 2020-12-03 03:53

    The source code for curry in the toolz library is available at the following link.

    https://github.com/pytoolz/toolz/blob/master/toolz/functoolz.py

    It handles args, kwargs, builtin functions, and error handling. It even wraps the docstrings back onto the curried object.

    0 讨论(0)
  • 2020-12-03 03:59

    I think I've got a better one:

    def curried (function):
        argc = function.__code__.co_argcount
    
        # Pointless to curry a function that can take no arguments
        if argc == 0:
            return function
    
        from functools import partial
        def func (*args):
            if len(args) >= argc:
                return function(*args)
            else:
                return partial(func, *args)
        return func
    

    This solution uses Python's own functools.partial function instead of effectively recreating that functionality. It also allows you to pass in more arguments than the minimum, -allows keyword arguments,- and just passes through functions that don't have to take arguments, since those are pointless to curry. (Granted, the programmer should know better than to curry zero-arity or multi-arity functions, but it's better than creating a new function in that case.)

    UPDATE: Whoops, the keyword argument part doesn't actually work right. Also, optional arguments are counted in the arity but *args are not. Weird.

    0 讨论(0)
  • 2020-12-03 04:00

    Many of the answers here fail to address the fact that a curried function should only take one argument.

    A quote from Wikipedia:

    In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument (partial application).

    Choosing to decorate it with recursion and without co_argcount makes for a decently elegant solution.

    from functools import partial, wraps, reduce
    
    def curry(f):
        @wraps(f)
        def _(arg):
            try:
                return f(arg)
            except TypeError:
                return curry(wraps(f)(partial(f, arg)))
        return _
    
    def uncurry(f):
        @wraps(f)
        def _(*args):
            return reduce(lambda x, y: x(y), args, f)
        return _
    

    As shown above, it is also fairly trivial to write an uncurry decorator. :) Unfortunately, the resulting uncurried function will allow any number of arguments instead of requiring a specific number of arguments, as may not be true of the original function, so it is not a true inverse of curry. The true inverse in this case would actually be something like unwrap, but it would require curry to use functools.wraps or something similar that sets a __wrapped__ attribute for each newly created function:

    def unwrap(f):
        try:
            return unwrap(f.__wrapped__)
        except AttributeError:
            return f
    
    0 讨论(0)
  • 2020-12-03 04:00

    This one is fairly simple and doesn't use inspect or examine the given function's args

    import functools
    
    
    def curried(func):
        """A decorator that curries the given function.
    
        @curried
        def a(b, c):
            return (b, c)
    
        a(c=1)(2)  # returns (2, 1)
        """
        @functools.wraps(func)
        def _curried(*args, **kwargs):
            return functools.partial(func, *args, **kwargs)
        return _curried
    
    0 讨论(0)
提交回复
热议问题