Функции Python 2.7 помнят значение, а не ссылку? Закрытие странность - PullRequest
3 голосов
/ 18 июля 2011

Я пытаюсь вернуть из функции список функций, каждая из которых использует переменные из внешней области видимости.Это не работаетВот пример, который демонстрирует, что происходит:

a = []
for i in range(10):
    a.append(lambda x: x+i)
a[1](1) # returns 10, where it seems it should return 2

Почему это происходит, и как я могу обойти это в python 2.7?

Ответы [ 3 ]

11 голосов
/ 18 июля 2011

i относится к одной и той же переменной каждый раз, поэтому i равно 9 во всех лямбдах, потому что это значение i в конце цикла. Самый простой обходной путь включает в себя аргумент по умолчанию:

lambda x, i=i: x+i

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

Другой обходной путь - определить лямбду, определяющую другую лямбду, и вызвать первую лямбду:

(lambda i: lambda x: x+i)(i)

Такое поведение имеет немного больше смысла, если учесть это:

def outerfunc():

    def innerfunc():
        return x+i

    a = []
    for i in range(10):
        a.append(innerfunc)
    return a

Здесь innerfunc определяется один раз, поэтому интуитивно понятно, что вы работаете только с одним функциональным объектом, и вы не ожидаете, что цикл создаст десять различных замыканий. С лямбдой это не выглядит как функция, определенная только один раз, похоже, что вы определяете ее заново каждый раз в цикле, но на самом деле она функционально совпадает с длинной версией .

4 голосов
/ 18 июля 2011

Потому что меня не оценивают, когда вы определяете анонимную функцию (лямбда-выражение), а когда она вызывается.Вы можете увидеть это, добавив del i перед a[1](1): вы получите NameError: global name 'i' is not defined в строке a[1](1).

Вам необходимо фиксировать значение i в лямбда-выражении каждый раз, напримеритак:

a = [lambda x, i=i: x+i for i in range(10)]
a[1](1) # returns 2
2 голосов
/ 18 июля 2011

Другое, более общее решение - также без лямбд:

import operator
from functools import partial
a = []
for i in range(10):
    a.append(partial(operator.add, i))
a[1][(1) # returns 2

Ключевой аспект здесь functools.partial .

...