Проблема:
Позвольте f1
, f2
быть двумя функциями x
и другим количеством других аргументов; то, что они на самом деле делают, не имеет значения, но я буду использовать дельта-функции Dira c для иллюстрации:
def f1(x, p1, p2, p3):
if x == p1:
return p3
elif x == p2:
return p3
else:
return 0
def f2(x, p1, p2):
if x == p1:
return p2
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, все выглядит хорошо, и этот код должен, по любым логикам c, возвращать 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
Удивительно ничего , кажется, идет не так. Это выходные данные всех этих print
s для i = 1
:
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
сохраняется.
Я в своем уме конец здесь. Что я сделал неправильно и как я могу сделать этот код функционировать должным образом? Есть ли более надежный и / или элегантный способ сделать это?