partial + lambda: почему результат [10, 11, 12]
@python_quizРазберем этот квиз
Коротко: код использует functools.partial, который фиксирует первые позиционные аргументы функции. В приведённом примере для каждой итерации создаётся частичная функция с фиксированным первым аргументом i, поэтому вызовы возвращают i + 10, то есть [10, 11, 12].
Далее подробно разберём, как это работает, почему некоторые другие варианты вывода встречаются в похожих задачах, и какие альтернативы/подводные камни стоит знать.
Исходный код

Вывод этой программы будет:

Пошаговый разбор
- lambda объявляет функцию с двумя позиционными аргументами: x и y, возвращающую x + y.
- partial(lambda x, y: x + y, i) фиксирует первый аргумент (x) значением i.
- partial создаёт новую функцию, ожидающую оставшиеся аргументы (в нашем случае y).
- В comprehension для i в range(3) создаются три частичные функции с
i = 0, 1, 2. - При вызове каждой частичной функции с аргументом 10 происходит вычисление i + 10, что даёт 10, 11 и 12.
Почему не [10, 10, 10]?
Такой результат был бы правдоподобен, если бы partial фиксировал второй аргумент (y), а первый аргумент всегда был 10. Но в коде partial фиксирует именно первый аргумент (x), а мы передаём 10 при вызове — это значение становится y.
Почему не [12, 12, 12]? Похожая ошибка с замыканиями
В похожих примерах часто встречается версия:

Её вывод в CPython будет:

Почему? Лямбда захватывает переменную i, а не её текущее значение. После окончания цикла i остаётся равным 2, и все функции используют это одно и то же значение при вызове.
Способы исправить эту проблему:
- Использовать параметр по умолчанию в lambda:

- Или применить functools.partial (как в исходном примере).
Почему не [10, 11, 13]?
Такой набор значений означал бы, что где-то произошло непоследовательное фиксирование аргументов (например, одна функция получила неверное i). В корректном коде с deterministic range и partial это не случится. Такой результат мог бы появиться только при ошибке в логике генерации функций или при побочных эффектах, которые изменяют i между созданием частичных функций и их вызовом — но в данном примере ничего такого нет.
Дополнительные примеры и сравнение
Покажем три варианта и их выводы:
1) Используем partial (оригинал):

2) Лямбда, захват переменной i (неправильно для ожидаемого результата):

3) Лямбда с параметром по умолчанию (фиксируем значение i при создании функции):

Выводы и рекомендации
- functools.partial — удобный способ фиксировать позиционные и именованные аргументы функции при создании её частичного варианта.
- При создании функций в цикле будьте внимательны: стандартные лямбды захватывают переменные по ссылке, что часто приводит к непредвиденным результатам. Чтобы зафиксировать текущее значение, используйте параметры по умолчанию или partial.
- Если нужно сохранить поведение «фиксировать значение сейчас», предпочтительнее явный способ (partial или
i=i), чтобы код был понятен и не подводил других разработчиков.
Эта задача — классический пример работы partial и распространённой ловушки замыканий в Python.