2009-07-10 2 views
14

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

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 

Любые идеи?

ответ

18

лямбды в питоне являются замыканиями .... аргументы вы даете это Арен» t будет оцениваться до оценки лямбда. В то время i = 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)) 
+0

Это должно быть functools.partial (f, i) – FogleBird

+0

Согласен.Частичное приложение - это путь сюда. –

+0

здесь частичное (f, i) означает частичное (f, b = i) не частичное (f, a = i). так что это не то же самое, что исходный пост. Приложение с частичной функцией «справа» (http://www.gossamer-threads.com/lists/python/dev/715103) было отклонено два раза. – sunqiang

2

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

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

Аналогично:

i = 9 
def foo(): 
    print i 

i = 10 
foo() 

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

10

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

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

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

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

13

Да, обычная «проблема определения области видимости» (на самом деле проблема связывания, а не того, что вам нужно, но ее часто называют этим именем). Вы уже получили два лучших (потому что самый простой) ответы - на «фальшивый» по умолчанию 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, но подход к заводским функциям стоит рассмотреть, когда вам нужно что-то немного богаче, связывая один аргумент.

+2

Я этого раньше не видел. Вид аккуратный. – ars

+0

@ars, бит переполнения для большинства случаев на самом деле (на Python), но это то, что можно было бы сделать естественно (скажем) Scheme, поэтому я предполагаю, что это _is_ kinda neat ;-). –

+0

Правильно, я не могу сразу придумать случай, где я бы предпочел, но тем не менее забавная конструкция. :-) – ars