2016-03-16 2 views
2

Я встречаюсь с каким-то странным поведением с лямбда-функциями в цикле в python. Когда я пытаюсь назначить лямбда-функции в словарных записях в списке, а когда другие слова в словаре используются в этой функции, только последний раз через цикл вычисляется лямбда-оператор. Таким образом, все функции имеют одинаковое значение!лямбда-операторов в петлях питона

Ниже приведен урезанный код, который захватывает только те части, что я пытаюсь, что ведет себя странно. Мой фактический код более сложный, не такой тривиальный, как этот, поэтому я ищу объяснение и, желательно, обходное решение.

n=4 
numbers=range(n) 
entries = [dict() for x in numbers] 

for number, entry in zip(numbers,entries): 
    n = number 
    entry["number"] = n 
    entry["number2"] = lambda x: n*1 

for number in numbers: 
    print(entries[number]["number"], entries[number]["number2"](2)) 

Выход:

0 3 
1 3 
2 3 
3 3 

Другими словами, словарь заходы, которые только целые числа в порядке, и были заполнены должным образом с помощью петли. Но функции лямбда - тривиальные и должны просто возвращать то же значение, что и записи «число» - это все, установленные до последнего прохода.

Что происходит?

ответ

1

Возможно, вы столкнулись с проблемой, что метод создан как ссылка на переменную n. Функция оценивается только после цикла, поэтому вы собираетесь вызвать функцию, которая ссылается на n. Если вы нормально с наличием функции оцененную в момент присвоения можно поместить вызов функции вокруг него:

(lambda x: n*1)(2) 

или если вы хотите иметь функции использовать, у них ссылки на конкретное значение, которое вы хотите , Из кода вы можете использовать аргумент по умолчанию в качестве обходного пути:

entry["number"] = n 
entry["number2"] = lambda x, n=n: n*1 

Разница сводится к вопросу адресации памяти. Я полагаю, что-то вроде этого:

You:  Python, please give me a variable called "n" 
Python: Ok! Here it is, it is at memory slot 1 
You:  Cool! I will now create functions which say take that variable "n" 
     value (at memory slot 1) and multiply it by 1 and return that to me. 
Python: Ok! Got it: 
      1. Take the value at memory slot 1. 
      2. Multiply by 1. 
      3. Return it to you. 
You:  Done with my looping, now evaluate those instructions! 
Python: Ok! Now I will take the value of at memory slot 1 and multiply by 1 
     and give that to you. 
You:  Hey, I wanted each function to reference different values! 
Python: I followed your instructions exactly! 
2

К концу вашего for цикла, переменная n - которая, в отличие от статических языков, таких как C#, устанавливается в 3, который затем осуществляется доступ в лямбда выражение. Значение переменной не фиксировано; поскольку another answer на сайте указывает, лямбда-выражения являются текучими и будут содержать ссылки на переменные, а не захватывать значения во время создания. This question также обсуждает вашу проблему.

Чтобы исправить это, вы должны дать Лямбды новые, локальные переменный с помощью параметров по умолчанию:

entry["number2"] = lambda x, n=n: n*1 

Это создает новую переменную в рамках лямбды, которая называется п, которая устанавливает значение по умолчанию на " вне "значения n. Обратите внимание, что это решение, одобренное official FAQ, как указано в this answer состояниями Adrien Plisson.

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

EDIT: Как было заявлено Sci Prog, это решение делает n = number избыточным.Ваш окончательный код будет выглядеть примерно так:

lim = 4 
numbers = range(lim) 
entries = [dict() for x in numbers] 

for number, entry in zip(numbers, entries): 
    entry["number"] = number 
    entry["number2"] = lambda x, n = number: n*1 

for number in numbers: 
    print(entries[number]["number"], entries[number]["number2"](2)) 
+0

хороший ответ, хотя я бы удалил комментарий: «# Необязательно: del lim, если это будет необходимо позже». Я не вижу необходимости развязывать ссылку «lim» из глобального пространства имен. За исключением некоторых случайных случаев, 'del' обычно считается плохим стилем и не используется. Для такого тривиального сценария отключение единственной ссылки на целое было бы нетрадиционным и беспристрастным. –

+0

Не собираюсь притворяться, что ничего не знаешь о соглашениях на высшем уровне питона. большинство моих знаний на питоне - это любитель. Благодарю. – BHustus

2

Попробуйте

N=4 
numbers=range(N) 
entries = [dict() for x in numbers] 

for number, entry in zip(numbers,entries): 
    entry["number"] = number 
    entry["number2"] = lambda x,n=number: n*1 

for number in numbers: 
    print(entries[number]["number"], entries[number]["number2"](2)) 

Он печатает (python3)

0 0 
1 1 
2 2 
3 3 

Чтобы избежать путаницы, n называют разные вещи в вашем коде. Я использовал его только в одном месте.

Это проблема с закрытием.

+0

У вас также есть ответ для BHustus, когда я печатал мой. Я поддержал его ответ. –

+0

+1 для указания избыточности использования n в этом решении. Помните, если я переработаю свой ответ, чтобы отразить это? – BHustus

+0

Без проблем, вперед. Это улучшает ясность. –

Смежные вопросы