老版functional.curry有没有精确替代品?
我正在尝试在 Python 2.6 环境中运行这个代码片段,来源于 http://www.ibm.com/developerworks/linux/library/l-prog3.html。
from functional import *
taxcalc = lambda income,rate,deduct: (income-(deduct))*rate
taxCurry = curry(taxcalc)
taxCurry = taxCurry(50000)
taxCurry = taxCurry(0.30)
taxCurry = taxCurry(10000)
print "Curried taxes due =",taxCurry
print "Curried expression taxes due =", \
curry(taxcalc)(50000)(0.30)(10000)
好的,我从 http://www.python.org/dev/peps/pep-0309/ 了解到,functional 被改名为 functools,而 curry 被改名为 partial,但仅仅改名字并没有解决问题。我遇到了这个错误:
taxCurry = taxCurry(50000)
TypeError: <lambda>() takes exactly 3 arguments (1 given)
下面的代码可以正常工作,但我真的需要改得这么多吗?
from functools import partial
taxcalc = lambda income,rate,deduct: (income-(deduct))*rate
taxCurry = partial(taxcalc)
taxCurry = partial(taxCurry, 50000)
taxCurry = partial(taxCurry, 0.30)
taxCurry = partial(taxCurry, 10000)
print "Curried taxes due =", taxCurry()
print "Curried expression taxes due =", \
taxcalc(50000, 0.30, 10000)
有没有更好的方法来保留原始示例的机制?最后,原始示例真的算是柯里化(currying)吗,还是只是部分应用(partial application)?(根据 http://www.uncarved.com/blog/not_currying.mrk)
谢谢你的时间
3 个回答
1
我写了一个可以使用的柯里化装饰器,效果很好:
def curry(func):
"""
Decorator to curry a function, typical usage:
>>> @curry
... def foo(a, b, c):
... return a + b + c
The function still work normally:
>>> foo(1, 2, 3)
6
And in various curried forms:
>>> foo(1)(2, 3)
6
>>> foo(1)(2)(3)
6
This also work with named arguments:
>>> foo(a=1)(b=2)(c=3)
6
>>> foo(b=1)(c=2)(a=3)
6
>>> foo(a=1, b=2)(c=3)
6
>>> foo(a=1)(b=2, c=3)
6
And you may also change your mind on named arguments,
But I don't know why you may want to do that:
>>> foo(a=1, b=0)(b=2, c=3)
6
Finally, if you give more parameters than expected, the exception
is the expected one, not some garbage produced by the currying
mechanism:
>>> foo(1, 2)(3, 4)
Traceback (most recent call last):
...
TypeError: foo() takes exactly 3 arguments (4 given)
"""
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
if __name__ == "__main__":
import doctest
doctest.testmod()
1
toolz
项目中的curry
实现可以直接替换使用。
$ pip install toolz
>>> from toolz import curry
2
我想他们之所以做出这个改变,是因为Python是一种动态类型的语言。这意味着如果原来的curry
代码出现问题,调试起来会非常困难——比起像Haskell这样的语言,Haskell会直接给你一个很清晰的类型错误提示。所以我觉得用更明确的partial
版本来替代它是个合理的决定(看起来更符合Python的风格)。
你的例子也有点奇怪,因为你只是把部分应用的函数重新赋值给了同一个名字。通常情况下,部分应用的函数应该传递给另一个函数。至少在我能想到的Python中,这才是合理的用法。