Программа доходит до строки print(mydoubler(11)). Чтобы напечать результат выполнения функции mydoubler, программа вызывает эту функцию с аргументом 11, для этого программа идёт к строке mydoubler = myfunc(2).
Ну вот тут ты уже неправ. Программа не идёт назад по коду.
Сначала отрабатывает оператор
def
, определяющий функцию
myfunc
. После этого в текущей области видимости программы появляется переменная
myfunc
, хранящая ссылку на объект-функцию.
Затем отрабатывает строка
mydoubler = myfunc(2)
. Происходит вызов функции
myfunc
, при этом ей передаётся объект-число 2 как параметр. Внутри локальной области видимости
myfunc
теперь есть имя
a
, ссылающееся на объект-число 2. В ходе выполнения создаётся лямбда-функция, которая ссылается на этот параметр.
Лямбда функция возвращается из
myfunc
,
myfunc
завершает выполнение. Поскольку лямбда-функция продолжает существовать (мы её вернули), а она ссылается на объект-число 2, то этот объект тоже продолжает существовать.
Возвращённая функция присваивается имени
mydoubler
в текущей области видимости. Это позволяет её продолжать существовать (её не собирает сборщик мусора), ну и позволяет её вызвать по этому имени.
Если бы ты сделал ещё один вызов, скажем,
mytripler = myfunc(3)
, это бы создало новую, отдельную лямбда-функцию, совершенно независимую от
mydoubler
, и ссылающуюся на объект-число 3.