Python functools.partial和functools.reduce之间的交互

Python functools.partial和functools.reduce之间的交互,python,python-3.x,functional-programming,Python,Python 3.x,Functional Programming,我有一个函数,我正试图将其制作成部分版本,然后在reduce语句中应用该部分函数 分部函数本身是有效的——因为我通常可以用一组参数调用它,它将返回正确的答案。但是,如果在“减少”下使用,则会断裂 比如, import functools test=[1,2,3,4] def base_function(mult=10,a=0,b=0): return(mult*(a+b)) #control function, should act like partial base_functi

我有一个函数,我正试图将其制作成部分版本,然后在reduce语句中应用该部分函数

分部函数本身是有效的——因为我通常可以用一组参数调用它,它将返回正确的答案。但是,如果在“减少”下使用,则会断裂

比如,

import functools

test=[1,2,3,4]

def base_function(mult=10,a=0,b=0):
    return(mult*(a+b))

#control function, should act like partial base_function with mult=1
def test_function1(a=0,b=0):
    return(a+b)

#stupid partial function that has destroyed my friday afternoon
test_function2=functools.partial(base_function,mult=1)
print(test_function2.args) #returns ()
print(test_function2.keywords) #returns {'mult':1} 

print(test_function1(a=1,b=2)) #returns 3 as expected
print(test_function2(a=1,b=2)) #returns 3 as expected! horray!

#so the partial function itself works when called normally

print(functools.reduce(test_function1,test,-20)) #returns -10
print(functools.reduce(test_function2,test,-20)) #returns TypeError: base_function() got multiple values for argument 'mult'
如果我更改代码使mult成为posistional参数并更新partial函数,这将是一个额外的好处

def base_function(mult,a=0,b=0):
    return(mult*(a+b))


def test_function1(a=0,b=0):
    return(a+b)

test_function2=functools.partial(base_function,1)
print(test_function2.args)
print(test_function2.keywords)
“血腥的东西”与reduce语句配合得非常好

谁能给我解释一下吗?对于这种类型的错误,我能找到的唯一答案似乎适用于有人将函数中的某个东西同时作为关键字和普通参数调用的情况——据我所知,这在这里没有发生过——如果是这种情况,则正常调用部分函数时,部分函数不应正常工作。使mult成为位置论证的“解决方案”是可行的,但我真的更想知道是什么导致了这个错误,因为显然我一定是误解了一些关键的东西

编辑:多亏了那些回答我问题的海报——我现在明白了为什么会发生这种情况,以及为什么重新排列参数或使它们定位可以解决问题,所以我将其标记为已解决

然而,我确实想知道是否有一种“简洁”的方法可以做到这一点——一个类似于partial的操作符,它不仅返回一个partial对象,如果参数传递正确,它的行为就像所需的函数一样,而且还直接返回一个任意所需的函数,而不需要预定义该任意函数。例如:

import functools

def base_function(mult=10,a=0,b=0):
    return(mult*(a+b))

def test_function1(a=0,b=0):
    return(a+b)

test_function2=partial_like_operator(base_function,mult=1)

print(test_function2==test_function1) #prints true

这一切都是按参数排序的。在您的测试用例中,与使用前两个参数的
reduce()
相比,使用
a=1,b=2显式绕过顺序,它确实会失败

然而,如果你扭转局面,这似乎是可行的:

def base_function(a=0, b=0, mult=10):
    return mult * (a + b)
至少这对我来说是个好办法

在您的情况下,以下操作也很可能失败:

print(test_function2(1, 2))
这是因为它尝试设置
mult
两次…

@horoy是正确的

另一种解释方式是:

在引擎盖下,对
reduce
的调用会出现错误,最后会出现如下调用:

test_function2(x, y, mult=1)
其中
x
y
reduce
函数的局部变量

您可以尝试一下,自己很容易发现这不是一个有效的呼叫。传递的第一个参数必须对应于
mult
,因为这是函数的第一个参数,但随后
mult
被传递另一个值,作为kw arg

在调用起作用的部分函数的测试中,您将
a
b
作为kwargs传递,从而绕过了这个问题


结论是,当使用partial时,最好以绑定函数的最后一个参数而不是第一个参数的方式进行设计。

谢谢,这是非常清楚的解释。是否有一个类似于partial的运算符,它的行为与我预期的一样,因此它返回一个适当的新函数,并将传递的参数内置到函数体中?因此,在上面的示例中,test_function 2实际上等于test_function 1(a,b),而不是base_function(mult=1,a,b)?@Eladriol,通过更改实现功能的顺序。请看我的答案,如果您将
mult
参数放在末尾,您将在哪里获得预期行为。我在问题中发布了示例代码,希望能更好地阐述我的意思。问题解决了,我理解你的答案,这更像是一种风格。@Eladriol,没有这样的功能。您可以尝试使用inspect.getcallargs实现一个函数,它提供了有关如何调用底层函数的所有信息。