Объединение нескольких лямбда-функций в одну
Предположим, есть список словарей, состоящих из лямбда функций:
funcs = [{'1': lambda a, b: a + b > 5}, {'1': lambda a, b: b > a},
{'2': lambda a, b: a * b > 10}, {'2': lambda a, b: a - b == 4}]
Нужно создать новый словарь с лямбда функциями таким образом, чтобы объединить через оператор 'and' все лямбда-функции, содержащиеся по данному ключу. Если сделать это "руками", то все работает как надо:
funcs_combine_1 = {}
funcs_combine_1['1'] = lambda a, b: funcs[0]['1'](a, b) and funcs[1]['1'](a, b)
funcs_combine_1['2'] = lambda a, b: funcs[2]['2'](a, b) and funcs[3]['2'](a, b)
print(funcs_combine_1['1'](3, 8))
print(funcs_combine_1['1'](1, 3))
print(funcs_combine_1['2'](6, 2))
print(funcs_combine_1['2'](5, 1))
True
False
True
False
А если попробовать написать код, чтобы автоматически находились и объединялись лямбда функции по ключам:
funcs_combine_2 = {'1': lambda a, b: True, '2': lambda a, b: True}
for val in funcs:
for key in val.keys():
funcs_combine_2[key] = lambda a, b: funcs_combine_2[key](a, b) and val[key](a, b)
то появляется ошибка вида:
funcs_combine_2[key] = lambda a, b: funcs_combine_2[key](a, b) and val[key](a, b)
^^^^^^^^^^^^^^^^^^^^^^^^^^
[Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
Как можно исправить этот код, чтобы лямбда функции объединялись по ключам через оператор 'and' автоматически?
Другими словами, из списка
funcs = [{'1': lambda a, b: a + b > 5}, {'1': lambda a, b: b > a},
{'2': lambda a, b: a * b > 10}, {'2': lambda a, b: a - b == 4}]
должен получиться список:
funcs = [{'1': lambda a, b: a + b > 5 and b > a},
{'2': lambda a, b: a * b > 10 and a - b == 4}]
Спасибо!!
Ответы (2 шт):
funcs_combine_2[key] = lambda a, b: funcs_combine_2[key](a, b) and val[key](a, b)
^^^^^^^^^^^^^^^ вызов самой себя ^^^^^^^^^^^^^^^
Лямбда выполняется "лениво". Поэтому вы фактически делаете функцию, которая будет вызывать сама себя. funcs_combine_2
когда она наконец-то будет вызвана, будет вызывать сама себя до бесконечности.
Вы можете создать новый словарь и дальше работать уже с ним, чтобы не было рекурсии:
funcs_combine_result = {}
for val in funcs:
for key in val.keys():
funcs_combine_result[key] = lambda a, b: funcs_combine_2[key](a, b) and val[key](a, b)
print(funcs_combine_result['1'](3, 8))
print(funcs_combine_result['1'](1, 3))
print(funcs_combine_result['2'](6, 2))
print(funcs_combine_result['2'](5, 1))
Вывод:
False
False
True
True
Ваш вариант:
...
funcs_combine_2[key] = lambda a, b: funcs_combine_2[key](a, b) and val[key](a, b)
...
здесь, в выражении lambda a, b: funcs_combine_2[key](a, b) and val[key](a, b)
захватывается объект funcs_combine_2, а не текущее значение funcs_combine_2[key]. Учитывая, что вы модифицируете значение funcs_combine_2, при вызове возникнет бесконечная рекурсия, поскольку в ходе вычисления funcs_combine_2[key](a, b) нужно вычислить funcs_combine_2[key](a, b).
Решение: захватывать не funcs_combine_2, а текущее значение funcs_combine_2[key], для чего ему нужно присвоить отдельное имя (создать временную переменную).
funcs_combine_2 = {'1': lambda a, b: True, '2': lambda a, b: True}
for val in funcs:
for key in val.keys():
old_lambda = funcs_combine_2[key]
new_lambda = val[key]
funcs_combine_2[key] = lambda a, b: old_lambda(a, b) and new_lambda(a, b)
В общем случае, чтобы не захватывать ничего лишнего и не мусорить в глобальное пространство имен (old_lambda/new_lambda...), полезно ввести функцию для логического склеивания лямбд:
funcs = [{'1': lambda a, b: a + b > 5}, {'1': lambda a, b: b > a},
{'2': lambda a, b: a * b > 10}, {'2': lambda a, b: a - b == 4}]
def combine_lambda(l1, l2):
return lambda a, b: l1(a, b) and l2(a, b)
funcs_combine_2 = {}
for val in funcs:
for key in val.keys():
funcs_combine_2[key] = combine_lambda(funcs_combine_2.get(key, lambda a, b: True), val[key])
print(funcs_combine_2['1'](3, 8))
print(funcs_combine_2['1'](1, 3))
print(funcs_combine_2['2'](6, 2))
print(funcs_combine_2['2'](5, 1))
True
False
True
False