我正在尝试在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)
任何有关如何正确执行此操作的指示将非常感谢!
谢谢!
以下实现是天真的,谷歌为“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)
curry
中toolz
library的源代码可从以下链接获得。
https://github.com/pytoolz/toolz/blob/master/toolz/functoolz.py
它处理args,kwargs,内置函数和错误处理。它甚至将文档字符串包裹回到咖喱对象上。
这里的许多答案都没有解决这个问题,即一个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
装饰器也是相当简单的。 :)不幸的是,由此产生的uncurried函数将允许任意数量的参数,而不是需要特定数量的参数,因为原始函数可能不是这样,因此它不是curry
的真正反转。在这种情况下,真正的倒数实际上是像unwrap
,但它需要curry
使用functools.wraps
或类似的东西为每个新创建的函数设置__wrapped__
属性:
def unwrap(f):
try:
return unwrap(f.__wrapped__)
except AttributeError:
return f
因为在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
这个很简单,不使用检查或检查给定函数的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
这是我的咖喱版本,不使用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
我想我有一个更好的:
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关键字参数更多的参数,并且只传递不必参数的函数,因为这些函数对于咖喱来说毫无意义。 (当然,程序员应该知道比调整零度或多元函数更好,但它比在这种情况下创建一个新函数更好。)
更新:哎呀,关键字参数部分实际上并不正常。此外,可选参数在arity中计算,但* args不计算。奇怪的。
在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']
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