Проблемы с лямбдой Python
Что здесь происходит? Я пытаюсь создать список функций:
def f(a,b):
return a*b
funcs = []
for i in range(0,10):
funcs.append(lambda x:f(i,x))
Это не делает то, что я ожидаю. Я бы ожидал, что список будет действовать следующим образом:
funcs[3](3) = 9
funcs[0](5) = 0
Но все функции в списке кажутся одинаковыми и устанавливают фиксированное значение равным 9:
funcs[3](3) = 27
funcs[3](1) = 9
funcs[2](6) = 54
Любые идеи?
Ответы
Ответ 1
lambdas в python - это замыкания... аргументы, которые вы ему даете, не будут оцениваться до тех пор, пока не будет оценена лямбда. В то время я = 9 независимо, потому что итерация завершена.
Поведение, которое вы ищете, может быть достигнуто с помощью functools.partial
import functools
def f(a,b):
return a*b
funcs = []
for i in range(0,10):
funcs.append(functools.partial(f,i))
Ответ 2
Да, обычная "проблема определения области видимости" (на самом деле проблема связывания, а не того, что вам нужно, но она часто называется этим именем). Вы уже получили два лучших (из-за самых простых) ответов - решение "поддельное по умолчанию" i=i
и functools.partial
, поэтому я предоставляю только третье из трех классических, "factory лямбда":
for i in range(0,10):
funcs.append((lambda i: lambda x: f(i, x))(i))
Лично я бы пошел с i=i
, если нет риска того, что функции в funcs
будут случайно вызваны с 2 параметрами вместо 1, но подход функции factory стоит рассмотреть, когда вам нужно что-то немного бит более богатый, чем просто предварительный привязкой одного аргумента.
Ответ 3
Там только один i
, который привязан к каждой лямбде, вопреки тому, что вы думаете. Это распространенная ошибка.
Один из способов получить то, что вы хотите:
for i in range(0,10):
funcs.append(lambda x, i=i: f(i, x))
Теперь вы создаете по умолчанию параметр i
в каждом лямбда-закрытии и привязываете к нему текущее значение переменной цикла i
.
Ответ 4
Все лямбды оказываются связанными с последним. См. Этот вопрос для более длительного ответа:
Как создать список Python lambdas (в режиме просмотра/цикла цикла)?
Ответ 5
Учитывая конечное значение i == 9
Как и любая хорошая функция python, она будет использовать значение переменной в области, которую она определила. Возможно, lambda: varname
(поскольку это языковая конструкция) связывается с именем, а не значением, и оценивает это имя во время выполнения?
Аналогично:
i = 9
def foo():
print i
i = 10
foo()
Мне было бы очень интересно узнать, что мой ответ правильный