Python 为什么';t functools.partial返回实函数(以及如何创建这样的函数)?
所以我在Python中使用curry函数,我注意到的一点是functools.partial返回的是partial对象,而不是实际的函数。让我恼火的一件事是,如果我做了以下事情:Python 为什么';t functools.partial返回实函数(以及如何创建这样的函数)?,python,function,partial,currying,Python,Function,Partial,Currying,所以我在Python中使用curry函数,我注意到的一点是functools.partial返回的是partial对象,而不是实际的函数。让我恼火的一件事是,如果我做了以下事情: five = partial(len, 'hello') five('something') 然后我们得到 TypeError: len() takes exactly 1 argument (2 given) 但我想发生的是 TypeError: five() takes no arguments (1 give
five = partial(len, 'hello')
five('something')
然后我们得到
TypeError: len() takes exactly 1 argument (2 given)
但我想发生的是
TypeError: five() takes no arguments (1 given)
有没有一个干净的方法让它像这样工作?我写了一个变通方法,但对我来说太粗糙了(对于使用varargs的函数还不起作用):
编辑:我想如果我添加更多的上下文可能会有所帮助。我正在编写一个装饰程序,它将自动实现如下功能:
@curry
def add(a, b, c):
return a + b + c
f = add(1, 2) # f is a function
assert f(5) == 8
我想隐藏这样一个事实,f是从一个部分(也许是个坏主意:p)创建的。上面的TypeError消息给出的消息是一个可以显示部分内容的示例。我想改变这一点
这需要推广,因此EnricoGiampieri和mgilson的建议仅适用于该特定情况。我的猜测是部分函数只是延迟了函数的执行,而不是从中创建一个全新的函数 我的猜测是,直接定义一个新函数更容易:
def five(): return len('hello')
这是一行非常简单的代码,不会使代码变得杂乱无章,而且非常清晰,因此我不会费心编写函数来替换它,特别是如果您在很多情况下不需要这种情况,您肯定不想使用
exec
您可以在纯Python中找到partial
的菜谱,例如-许多菜谱被错误地标记为curry
菜谱,所以也要寻找它们。无论如何,这些将向您展示在不使用exec
的情况下执行此操作的正确方法,您只需选择一个并对其进行修改即可
或者你可以只包装部分
然而,无论您做什么,包装器都无法知道它正在定义一个名为“五”的函数;这只是存储函数的变量的名称。因此,如果需要自定义名称,则必须将其传递给函数:
five = my_partial('five', len, 'hello')
在这一点上,您必须想知道为什么这比定义一个新函数更好
然而,我并不认为这是你真正想要的。您的最终目标是定义一个@curry
装饰器,该装饰器创建装饰函数的一个curry版本,其名称(以及docstring、arg list等)与装饰函数相同。替换中间partial
名称的整个想法都是在转移注意力;在curry
函数中正确使用,无论您如何定义curry函数,它都会保留原始函数的名称
在某些情况下,functools.wrapps
不起作用。事实上,这可能是您需要修改arg列表的次数之一,例如,curry(len)
可以接受0或1个参数,而不需要1个参数,对吗?请参阅和(非常简单的)forwrapps
和update_wrapper
,了解基础知识的工作原理,并从中构建
扩展上一个:要实现函数,您几乎必须返回一些内容,这些内容接受(*args)
或(*args,**kw)
并显式解析args,还可能显式引发类型错误
和其他适当的异常。为什么?如果foo
接受3个参数,curry(foo)
接受0、1、2或3个参数,如果给定0-2个参数,则返回一个接受0到n-1个参数的函数
您可能需要**kw
的原因是,它允许调用方按名称指定参数,尽管在累积参数后,检查参数会变得复杂得多,可以说这是一件很奇怪的事情,使用curry时,最好先用partial
绑定命名的参数,然后curry
结果并以curry样式传入所有剩余参数
如果foo
有默认值或关键字args,它会变得更加复杂,但即使没有这些问题,您也需要处理这个问题
例如,假设您将curry
实现为一个类,该类将函数和所有已经curry的参数作为实例成员。然后你会有这样的东西:
def __call__(self, *args):
if len(args) + len(self.curried_args) > self.fn.func_code.co_argcount:
raise TypeError('%s() takes exactly %d arguments (%d given)' %
(self.fn.func_name, self.fn.func_code.co_argcount,
len(args) + len(self.curried_args)))
self.curried_args += args
if len(self.curried_args) == self.fn.func_code.co_argcount:
return self.fn(*self.curried_args)
else:
return self
这非常简单,但它展示了如何处理基本问题。lambda:len(“hello”)有什么问题吗??我总是对
functools.partial
在lambda
之上和之外所做的事情有点模糊。…@mgilson:嗯,这会让错误消息变得更糟。而不是TypeError:len()只接受1个参数(给定2个)
您将得到TypeError:()接受0个位置参数,但得到1个位置参数
@mgilson:如果您有两个函数,每个函数接受不同数量的参数,会发生什么。一个lambda
需要事先准确地知道需要多少个参数……当然,除非您将参数包装在一个元组中,然后在lambda
@mgilson中解压它们:看看我上面的编辑是否回答了这个问题that@abarnert这不正是OP所说的吗?“但我想做的是:TypeError:five()不接受任何参数(给定1个)
”?我使用exec的原因是我需要定义一个只接受X个参数的函数,但直到运行时我才知道X是什么。(好吧,这不完全正确。我可以将参数的数量作为参数传递给我的curry decorator(请参见编辑),但我想避免这种情况)@epsilon:您可以通过检查发现,或者……实际上,查看现有functools
内容(适用于2.7)中的代码,还有一些PyPI模块,作为一些关于什么是可能的想法的配方,以及什么是做可能的事情的合理方法。我可以通过func.func_code.co_argcount,bu找到值
def __call__(self, *args):
if len(args) + len(self.curried_args) > self.fn.func_code.co_argcount:
raise TypeError('%s() takes exactly %d arguments (%d given)' %
(self.fn.func_name, self.fn.func_code.co_argcount,
len(args) + len(self.curried_args)))
self.curried_args += args
if len(self.curried_args) == self.fn.func_code.co_argcount:
return self.fn(*self.curried_args)
else:
return self