Почему результаты отображения map () и списка различаются?

Следующий тест не проходит:

#!/usr/bin/env python
def f(*args):
    """
    >>> t = 1, -1
    >>> f(*map(lambda i: lambda: i, t))
    [1, -1]
    >>> f(*(lambda: i for i in t)) # -> [-1, -1]
    [1, -1]
    >>> f(*[lambda: i for i in t]) # -> [-1, -1]
    [1, -1]
    """
    alist = [a() for a in args]
    print(alist)

if __name__ == '__main__':
    import doctest; doctest.testmod()

Другими словами:

>>> t = 1, -1
>>> args = []
>>> for i in t:
...   args.append(lambda: i)
...
>>> map(lambda a: a(), args)
[-1, -1]
>>> args = []
>>> for i in t:
...   args.append((lambda i: lambda: i)(i))
...
>>> map(lambda a: a(), args)
[1, -1]
>>> args = []
>>> for i in t:
...   args.append(lambda i=i: i)
...
>>> map(lambda a: a(), args)
[1, -1]

Для таких, как я, прочитать вопрос, но сначала не замечают никаких проблем: обратите внимание на [-1, -1]! По сути, lambda i: ... в цикле не фиксирует текущее значение i.

Beni Cherniavsky-Paskin 07.01.2010 20:27
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
12
2
2 406
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Лямбда захватывает переменные, а не значения, поэтому код

lambda : i

всегда будет возвращать значение i, к которому привязано В данный момент в замыкании. К моменту вызова это значение было установлено на -1.

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

>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1]
[1, -1]
Ответ принят как подходящий

Они разные, потому что значение i как в выражении генератора, так и в списке comp вычисляется лениво, то есть когда анонимные функции вызываются в f.
К этому времени i будет привязан к последнему значению t, которое равно -1.

Итак, в основном, это то, что делает понимание списка (как и для genexp):

x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)

Теперь лямбды содержат замыкание, которое ссылается на i, но i привязан к -1 в обоих случаях, потому что это последнее значение, которому он был назначен.

Если вы хотите убедиться, что лямбда получает текущее значение i, выполните

f(*[lambda u=i: u for i in t])

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

Редактировать: есть одно различие между выражениями генератора и пониманием списков: последнее пропускает переменную цикла в окружающую область видимости.

Лямбды - зло, потому что неясно, что такое контекст среды выполнения на самом деле.

S.Lott 27.09.2008 00:48

@ S.Lott: Обычные функции в Python ничем не отличаются. def f(): return i Вы не знаете, что такое i, независимо от функции или лямбда-выражения.

jfs 28.10.2008 01:40

Выражение f = lambda: i эквивалентно:

def f():
    return i

Выражение g = lambda i=i: i эквивалентно:

def g(i=i):
    return i

i - это свободная переменная в первом случае, и он привязан к параметру функции во втором случае, то есть в этом случае это локальная переменная. Значения параметров по умолчанию оцениваются во время определения функции.

Выражение генератора является ближайшей охватывающей областью (где определен i) для имени i в выражении lambda, поэтому i разрешается в этом блоке:

f(*(lambda: i for i in (1, -1)) # -> [-1, -1]

i - это локальная переменная блока lambda i: ..., поэтому объект, на который она ссылается, определен в этом блоке:

f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]

Другие вопросы по теме