Производящие функции в цикле с лямбда-выражением в Python

Если я составляю два списка функций:

def makeFun(i):
    return lambda: i

a = [makeFun(i) for i in range(10)]
b = [lambda: i for i in range(10)]

почему делают списки a и b не ведут себя в сохранении путь?

Например:

>>> a[2]()
2
>>> b[2]()
9
29
задан Akaisteph7 12 July 2019 в 20:03
поделиться

5 ответов

Технически лямбда-выражение закрыто над i , видимым в глобальной области, которая в последний раз установлена ​​на 9. Это ] same i упоминается во всех 10 лямбдах. Например,

i = 13
print b[3]()

В функции makeFun лямбда закрывается для i , который определяется при вызове функции. Это десять разных и сек.

20
ответ дан 28 November 2019 в 01:32
поделиться

Один набор функций (a) работает с переданным аргументом, а другой (b) работает с глобальной переменной, которая затем устанавливается в 9. Проверьте разборку:

>>> import dis
>>> dis.dis(a[2])
  1           0 LOAD_DEREF               0 (i)
              3 RETURN_VALUE
>>> dis.dis(b[2])
  1           0 LOAD_GLOBAL              0 (i)
              3 RETURN_VALUE
>>>
7
ответ дан 28 November 2019 в 01:32
поделиться

Лямбды в Python разделяют область видимости переменных, в которой они созданы. В вашем первом случае область действия лямбда - это makeFun. Во втором случае это глобальный i , который равен 9, потому что это остаток цикла.

В любом случае это то, что я понимаю ...

1
ответ дан 28 November 2019 в 01:32
поделиться

Хороший улов. Лямбда в понимании списка каждый раз видит одно и то же local i .

Вы можете переписать это как:

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

b = []
for i in range(10):
    b.append(lambda: i)

с тем же результатом.

1
ответ дан 28 November 2019 в 01:32
поделиться

Как утверждали другие, проблема в области видимости. Обратите внимание, что вы можете решить эту проблему, добавив дополнительный аргумент к лямбда-выражению и присвоив ему значение по умолчанию:

>> def makeFun(i): return lambda: i
... 
>>> a = [makeFun(i) for i in range(10)]
>>> b = [lambda: i for i in range(10)]
>>> c = [lambda i=i: i for i in range(10)]  # <-- Observe the use of i=i
>>> a[2](), b[2](), c[2]()
(2, 9, 2)

В результате i теперь явно помещается в область, ограниченную лямбда-выражением выражение.

21
ответ дан 28 November 2019 в 01:32
поделиться