partial + lambda: почему результат [10, 11, 12]

partial + lambda: почему результат [10, 11, 12]

@python_quiz

Разберем этот квиз

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

Далее подробно разберём, как это работает, почему некоторые другие варианты вывода встречаются в похожих задачах, и какие альтернативы/подводные камни стоит знать.

Исходный код

python

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

text

Пошаговый разбор

  1. lambda объявляет функцию с двумя позиционными аргументами: x и y, возвращающую x + y.
  2. partial(lambda x, y: x + y, i) фиксирует первый аргумент (x) значением i.

- partial создаёт новую функцию, ожидающую оставшиеся аргументы (в нашем случае y).

  1. В comprehension для i в range(3) создаются три частичные функции с i = 0, 1, 2.
  2. При вызове каждой частичной функции с аргументом 10 происходит вычисление i + 10, что даёт 10, 11 и 12.

Почему не [10, 10, 10]?

Такой результат был бы правдоподобен, если бы partial фиксировал второй аргумент (y), а первый аргумент всегда был 10. Но в коде partial фиксирует именно первый аргумент (x), а мы передаём 10 при вызове — это значение становится y.

Почему не [12, 12, 12]? Похожая ошибка с замыканиями

В похожих примерах часто встречается версия:

python

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

text

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

Способы исправить эту проблему:

  • Использовать параметр по умолчанию в lambda:
python
  • Или применить functools.partial (как в исходном примере).

Почему не [10, 11, 13]?

Такой набор значений означал бы, что где-то произошло непоследовательное фиксирование аргументов (например, одна функция получила неверное i). В корректном коде с deterministic range и partial это не случится. Такой результат мог бы появиться только при ошибке в логике генерации функций или при побочных эффектах, которые изменяют i между созданием частичных функций и их вызовом — но в данном примере ничего такого нет.

Дополнительные примеры и сравнение

Покажем три варианта и их выводы:

1) Используем partial (оригинал):

python

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

python

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

python

Выводы и рекомендации

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

Эта задача — классический пример работы partial и распространённой ловушки замыканий в Python.

Report Page