Проблемы с лямбдой 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.

Ответ 5

Учитывая конечное значение i == 9

Как и любая хорошая функция python, она будет использовать значение переменной в области, которую она определила. Возможно, lambda: varname (поскольку это языковая конструкция) связывается с именем, а не значением, и оценивает это имя во время выполнения?

Аналогично:

i = 9
def foo():
    print i

i = 10
foo()

Мне было бы очень интересно узнать, что мой ответ правильный