eps*_*lon 9 python function currying partial
所以我正在玩Python中的currying函数,我注意到的一件事就是functools.partial返回一个部分对象而不是一个实际的函数.令我恼火的事情之一是,如果我按照以下方式做了一些事情:
five = partial(len, 'hello')
five('something')
Run Code Online (Sandbox Code Playgroud)
然后我们得到
TypeError: len() takes exactly 1 argument (2 given)
Run Code Online (Sandbox Code Playgroud)
但我想要发生的是
TypeError: five() takes no arguments (1 given)
Run Code Online (Sandbox Code Playgroud)
是否有一种干净的方式让它像这样工作?我写了一个解决方法,但它对我的口味来说太过于hacky(对于使用varargs的函数还没有用):
def mypartial(f, *args):
argcount = f.func_code.co_argcount - len(args)
params = ''.join('a' + str(i) + ',' for i in xrange(argcount))
code = '''
def func(f, args):
def %s(%s):
return f(*(args+(%s)))
return %s
''' % (f.func_name, params, params, f.func_name)
exec code in locals()
return func(f, args)
Run Code Online (Sandbox Code Playgroud)
编辑:我认为如果我添加更多上下文可能会有所帮助.我正在写一个装饰器,它会自动调整一个这样的函数:
@curry
def add(a, b, c):
return a + b + c
f = add(1, 2) # f is a function
assert f(5) == 8
Run Code Online (Sandbox Code Playgroud)
我想隐瞒f是从部分创建的事实(也许是一个坏主意:P).上面给出的TypeError消息给出的消息是一个示例,其中可以显示某些内容是否为partial.我想改变这一点.
这需要推广,因此EnricoGiampieri和mgilson的建议仅适用于该特定情况.
您绝对不想使用 来执行此操作exec。
你可以找到partial纯Python的菜谱,比如这个\xe2\x80\x94,其中很多都被错误地标记为curry菜谱,所以也要寻找它。无论如何,这些将向您展示在没有 的情况下执行此操作的正确方法exec,您可以选择一个并修改它以执行您想要的操作。
或者你可以只包装partial\xe2\x80\xa6
然而,无论你做什么,包装器都无法知道它正在定义一个名为“五”的函数;这只是存储函数的变量的名称。因此,如果您想要自定义名称,则必须将其传递给函数:
\n\nfive = my_partial(\'five\', len, \'hello\')\nRun Code Online (Sandbox Code Playgroud)\n\n那时,您必须想知道为什么这比定义一个新函数更好。
\n\n但是,我认为这并不是您真正想要的。您的最终目标是定义一个@curry装饰器,该装饰器创建装饰函数的柯里化版本,其名称(以及文档字符串、参数列表等)与装饰函数相同。替换中间体名称的整个想法partial是一个转移注意力的想法;functools.wraps在你的函数中正确使用curry,无论你如何定义柯里化函数,它都会保留原始函数的名称。
在某些情况下,functools.wraps不起作用。事实上,这可能是其中之一\xe2\x80\x94,例如,您需要修改 arg 列表,因此curry(len)可以采用 0 或 1 个参数,而不是需要 1 个参数,对吗?请参阅和的update_wrapper(非常简单的)源代码,wraps了解update_wrapper基础知识如何工作,并从那里进行构建。
扩展上一个:要柯里化一个函数,你几乎必须返回一些显式接受(*args)或(*args, **kw)解析参数的东西,并且可能显式引发TypeError和其他适当的异常。为什么?好吧,如果foo接受 3 个参数,curry(foo)则接受 0、1、2 或 3 个参数,如果给定 0-2 个参数,则返回一个接受 0 到 n-1 个参数的函数。
您可能想要的原因**kw是它允许调用者通过名称 \xe2\x80\x94 指定参数,尽管这样当您完成累积参数时检查就会变得更加复杂,并且可以说这对于柯里化来说是一件奇怪的事情\ xe2\x80\x94最好首先将命名参数与 绑定partial,然后将curry结果绑定,并以柯里化风格传入所有剩余参数\xe2\x80\xa6
如果foo有默认值或关键字参数,它会变得更加复杂,但即使没有这些问题,您也已经需要处理这个问题。
例如,假设您实现curry为一个类,该类将函数和所有已柯里化的参数保存为实例成员。然后你会得到这样的东西:
def __call__(self, *args):\n if len(args) + len(self.curried_args) > self.fn.func_code.co_argcount:\n raise TypeError(\'%s() takes exactly %d arguments (%d given)\' %\n (self.fn.func_name, self.fn.func_code.co_argcount,\n len(args) + len(self.curried_args)))\n self.curried_args += args\n if len(self.curried_args) == self.fn.func_code.co_argcount:\n return self.fn(*self.curried_args)\n else:\n return self\nRun Code Online (Sandbox Code Playgroud)\n\n这过于简单化了,但它展示了如何处理基础知识。
\n