在python的Currying装饰员

时间:2012-02-26 23:21:50

标签: python decorator currying

我正在尝试在python中编写一个currying装饰器,我想我已经有了一般的想法,但仍然有一些不能正常工作的情况......

def curry(fun):

    cache = []
    numargs = fun.func_code.co_argcount

    def new_fun(*args, **kwargs):
        print args
        print kwargs
        cache.extend(list(args))

        if len(cache) >= numargs:   # easier to do it explicitly than with exceptions

            temp = []
            for _ in xrange(numargs):
                temp.append(cache.pop())
            fun(*temp)

    return new_fun


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

虽然对于以下情况,这样可以正常工作:

myfun(5)
myfun(5)

对于以下情况,它失败了:

myfun(6)(7)

非常感谢任何有关如何正确执行此操作的指示!

谢谢!

9 个答案:

答案 0 :(得分:26)

下面的实现是天真的,google为“currying python”提供更准确的示例。

def curry(x, argc=None):
    if argc is None:
        argc = x.func_code.co_argcount
    def p(*a):
        if len(a) == argc:
            return x(*a)
        def q(*b):
            return x(*(a + b))
        return curry(q, argc - len(a))
    return p

@curry
def myfun(a,b,c):
    print '%d-%d-%d' % (a,b,c)



myfun(11,22,33)
myfun(44,55)(66)
myfun(77)(88)(99)

答案 1 :(得分:6)

toolz librarycurry的源代码可在以下链接中找到。

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

它处理args,kwargs,内置函数和错误处理。它甚至将文档字符串包裹回到咖喱对象上。

答案 2 :(得分:3)

这里的许多答案都没有解决这个问题,即一个curried函数应该只接受一个参数。

来自Wikipedia的引用:

  

在数学和计算机科学中,currying是技术   翻译带有多个参数的函数的求值   (或一组参数)评估一系列函数,   每个都有一个参数(部分申请)。

选择使用递归来装饰它,而不使用 co_argcount 可以提供一个非常优雅的解决方案。

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 _

如上所示,编写uncurry装饰器也是相当简单的。 :)不幸的是,由此产生的未经处理的函数将允许任意数量的参数,而不需要特定数量的参数,因为原始函数可能不是这样,因此它不是curry的真正反转。在这种情况下,真正的倒数实际上类似于unwrap,但需要curry使用functools.wraps或类似的东西为每个新创建的函数设置__wrapped__属性:

def unwrap(f):
    try:
        return unwrap(f.__wrapped__)
    except AttributeError:
        return f

答案 3 :(得分:1)

在python中编写currying装饰器很酷,我试过了我的: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

答案 4 :(得分:1)

这是我的咖喱版本,不使用partial,并使所有函数只接受一个参数:

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

答案 5 :(得分:0)

我想我有一个更好的:

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

此解决方案使用Python自己的functools.partial函数,而不是有效地重新创建该功能。它还允许您传递比最小值-allows关键字参数更多的参数,并且只传递不必参数的函数,因为这些函数对于咖喱来说毫无意义。 (当然,程序员应该知道比调整零度或多元函数更好,但是在这种情况下创建一个新函数要好。)

UPDATE:糟糕,关键字参数部分实际上无法正常工作。此外,可选参数在arity中计算,但* args不计算。怪异。

答案 6 :(得分:0)

这个很简单,并且没有使用检查或检查给定的功能' 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

答案 7 :(得分:0)

在python中调用函数的最简单方法是这样的:

from functools import partial
curry = lambda f, g: partial(
    lambda F, G, *args, **kwargs: F(G(*args,**kwargs)),
    f, g
)

https://gist.github.com/hkupty/0ba733c0374964d41dec

可以按如下方式使用它:

_list = []
mask = "Test {}"
append_masked = curry(_list.append, mask.format)
for i in range(10):
    append_masked(i)

将产生:

['Test 1', 'Test 2', 'Test 3' ... 'Test 10']

答案 8 :(得分:0)

Roger Christman的解决方案不适用于每个星座。 我应用了一个小修补程序来处理这种情况:

curried_func(1)(2,3)

使它适用于每个星座的小修复在于返回的lambda:

def curried(func):
    def curry(*args):
        if len(args) == func.__code__.co_argcount:
            ans = func(*args)
            return ans
        else:
            return lambda *x: curry(*(args+x))
    return curry