Python лямбда-проблемы - PullRequest
15 голосов
/ 10 июля 2009

Что здесь происходит? Я пытаюсь создать список функций:

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

Есть идеи?

Ответы [ 5 ]

19 голосов
/ 10 июля 2009

лямбды в 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))
13 голосов
/ 10 июля 2009

Да, обычная «проблема с областями видимости» (на самом деле проблема с привязкой позже, чем вы хотите, но ее часто называют именно этим именем). Вы уже получили два лучших (потому что самых простых) ответа - «поддельное решение по умолчанию» i=i и functools.partial, поэтому я даю только третий из классических трех - «заводская лямбда»:

for i in range(0,10):
    funcs.append((lambda i: lambda x: f(i, x))(i))

Лично я бы пошел с i=i, если бы не было риска случайного вызова функций в funcs с 2 параметрами вместо 1, но подход к заводским функциям стоит рассмотреть, когда вам нужно что-то более богатое чем просто предварительное связывание одного аргумента.

11 голосов
/ 10 июля 2009

Есть только один i, который привязан к каждой лямбде, вопреки тому, что вы думаете. Это распространенная ошибка.

Один из способов получить то, что вы хотите:

for i in range(0,10):
    funcs.append(lambda x, i=i: f(i, x))

Теперь вы создаете параметр по умолчанию i в каждом лямбда-замыкании и привязываете к нему текущее значение переменной цикла i.

2 голосов
/ 10 июля 2009

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

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

Аналогично:

i = 9
def foo():
    print i

i = 10
foo()

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

2 голосов
/ 10 июля 2009

Все лямбды заканчивают тем, что были связаны с последним. Смотрите этот вопрос для более длинного ответа:

Как создать список лямбда-выражений Python (в цикле понимания списка / для цикла)?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...