类型错误 压缩和应用函数列表和相应的参数



问题:

f1f2是两个x函数和不同数量的其他参数;它们实际做什么无关紧要,但我将使用增量函数进行说明:

def f1(x, p1, p2, v):
if   x == p1:
return v
elif x == p2:
return v
else:
return 0
def f2(x, p1, v):
if   x == p1:
return v
else:
return 0

我现在想构造一个函数,该函数返回x函数列表,这些函数是两个函数的各种组合的总和:

import itertools
def choice_matrix(n):
return itertools.product(*[range(n)]*n)
def f(fn1, fn2, n):
aux1 = [[fn1 if ae == 0 else fn2 for ae in ar] for ar in choice_matrix(n)]
aux2 = [
lambda x, args: sum(
[fn(x, *arg) for fn, arg in zip(fns, args)]
) for fns in aux1
]
return aux2

其中choice_matrix只是一个函数,它返回n位置的两个元素的各种组合:

>>> cm = list(choice_matrix(2))
>>> for i in range(len(cm)): print(cm)
(0, 0)
(0, 1)
(1, 0)
(1, 1)

通过从函数打印,我可以看到aux1按预期工作,并且确实返回了一个函数矩阵:

[<function f1 at 0x7efbdec08730>, <function f1 at 0x7efbdec08730>]
[<function f1 at 0x7efbdec08730>, <function f2 at 0x7efbdec087b8>]
[<function f2 at 0x7efbdec087b8>, <function f1 at 0x7efbdec08730>]
[<function f2 at 0x7efbdec087b8>, <function f2 at 0x7efbdec087b8>]

作为测试,我现在尝试使用一些参数打印返回列表的第二个函数(所以f1 + f2(:

vs = [(1., 2., 3.), (4., 5.)]
test0 = f(f1, f2, 2)
test = test0[1]
print(test(1., vs))

f1有 4 个参数,f2有 3 个参数,一切似乎都很好,根据任何逻辑,这段代码都应该返回3.0。然而,我得到的是一个TypeError;该函数采用的位置参数比给定的要少:

Traceback (most recent call last):
File "test.py", line 323, in <module>
print(test(1., vs))
File "test.py", line 283, in <lambda>
arg in zip(aux1[i], args)])
File "test.py", line 283, in <listcomp>
arg in zip(aux1[i], args)])
TypeError: f2() takes 3 positional arguments but 4 were given

到目前为止我尝试过:

  • 类似的列表理解:
def f(fn1, fn2, n):
aux1 = [[fn1 if ae == 0 else fn2 for ae in ar] for ar in choice_matrix(n)]
for i in range(len(aux1)): print(aux1[i])
aux2 = [
lambda x, args: list(
map(
lambda fn, arg: fn(x, *arg), fns, args
)
) for fns in aux1
]
return aux2

这给出了相同的结果。

  • 完全解构aux2其基本组件操作,看看出了什么问题:
def f(fn1, fn2, n):
aux1 = [[fn1 if ae == 0 else fn2 for ae in ar] for ar in choice_matrix(n)]
aux2 = {}
for i in range(len(aux1)):
print(i)
print(aux1[i])
print(set(zip(aux1[i], vs)))
aux2[i] = lambda x, args: sum([fn(x, *arg) for fn, 
arg in zip(aux1[i], args)])
if i == 1: fun = aux2[i]; print(fun(1., vs))
print('')
return aux2

令人惊讶的是,似乎没有任何问题。这是i = 1的所有print的输出:

1
[<function f1 at 0x7efbdef23730>, <function f2 at 0x7efbdef237b8>]
{(<function f2 at 0x7efbdef237b8>, (4.0, 5.0)), (<function f1 at 0x7efbdef23730>, (1.0, 2.0, 3.0))}
3.0

因此,函数及其参数排列在一起以正确压缩,正确压缩,正确应用并返回预期值,但我仍然得到完全相同的TypeError因此,一旦参数传递到函数外部,压缩似乎就会中断。

  • 避免压缩的伪函数重写:
def f(fn1, fn2, n):
aux1 = [[fn1 if ae == 0 else fn2 for ae in ar] for ar in choice_matrix(n)]
def a(fns, x, args):
if len(fns) == 0 or len(args) == 0:
return []
else:
fhead, *ftail = fns
ahead, *atail = args
return [fhead(x, *ahead)] + a(ftail, x, atail)
for i in range(len(aux1)):
print('i =', i)
print(aux1[i])
if i == 1: print(a(aux1[i], 1., vs)); print(sum(a(aux1[i], 1., vs)))
print('')
aux2 = [lambda x, args: a(fs, x, args) for fs in aux1]
return aux2

内部打印输出给出了预期的结果:

i = 1
[<function f1 at 0x7efbdec19730>, <function f2 at 0x7efbdec197b8>]
[3.0, 0]
3.0

然而,TypeError仍然存在。

我在这里已经穷到了尽头。我做错了什么,如何使这段代码正常运行?有没有更强大和/或更优雅的方法?

这是一个常见的错误。Python 使用词法范围闭包,因此在嵌套列表推导中,lambda 会捕获fn作为外部列表推导中的局部变量:

aux2 = [lambda x, args: sum(
[fn(x, *arg) for fn, arg in zip(fns, args)]
) for fns in aux1]

此变量fns.

但是当lambda被评估时,你已经完成了对aux1的迭代,所以fns指的是aux1为所有lambda产生的最后一个值。

您必须注意在循环中创建闭包。修复很简单,创建另一个封闭范围:

def f(fn1, fn2, n):
aux1 = [[fn1 if ae == 0 else fn2 for ae in ar] for ar in choice_matrix(n)]
aux2 = [(lambda _fns: lambda x, args: sum(
[fn(x, *arg) for fn, arg in zip(_fns, args)]
))(fns)
for fns in aux1]
return aux2

我敦促你不要那么简洁,python 风格对于任何习惯的人来说都是相对冗长的,比如说,Haskell。另外,请注意,sum参数可以是生成器表达式,以免在将整个列表传递给sum之前实现整个列表,就像您需要列表理解一样(它被急切地评估(:

aux2 = [
(lambda _fns: lambda x, args: 
sum( fn(x, *arg) for fn, arg in zip(_fns, args) )
)(fns)
for fns in aux1
]

最新更新