Смотри, вот тебе бытовая аналогия для разгона. Представь обычную отвёртку. Её можно держать в руке и крутить шурупы. А можно положить в ящик с инструментами. Можно передать соседу. Можно даже подписать сверху стикером "моя лучшая отвёртка". Отвёртка при этом остаётся отвёрткой и продолжает делать своё дело, где бы она физически ни находилась.
В Python функция устроена точно так же. Это не какая-то особая магическая конструкция, которая существует только в момент вызова. Это самый обычный объект, такой же как число, строка или список. Его можно положить в переменную, сложить в список, передать в другую функцию как аргумент и даже вернуть из функции как результат.
Вот, собственно, и всё определение того, что называют "функции как объекты первого класса" (first-class functions). Первый класс здесь не про элитность, а про то, что с функцией можно делать всё то же самое, что и с любым другим значением в языке.
Вывод:
Привет, Мир!
<class 'function'>
Обрати внимание: say_hello = greet, без скобок. Скобки после имени функции означают "вызови меня прямо сейчас". Без скобок greet - это просто ссылка на объект-функцию, ту самую отвёртку, которую мы кладём в другую переменную. Именно поэтому в Python возможна такая штука: say_hello и greet указывают на один и тот же объект в памяти, оба вызова эквивалентны.
Вывод:
greet
upper
len
Функция здесь ведёт себя как любой другой элемент коллекции. У неё, кстати, есть собственные атрибуты, например __name__ - это тоже просто атрибут объекта, как .append у списка.
Вот это уже интереснее и ведёт нас прямиком к понятию функции высшего порядка (higher-order function). Функция высшего порядка - это функция, которая либо принимает другую функцию как аргумент, либо возвращает функцию как результат (либо и то, и другое сразу).
Вывод:
12
Смотри, что тут произошло. apply_twice ничего не знает заранее про increment. Она просто берёт любую функцию, которую ей передали как аргумент func, и вызывает её два раза подряд. Сначала increment(10) дало 11, потом increment(11) дало 12. Мы могли бы передать туда любую другую функцию с одним аргументом, и apply_twice бы её отработала.
Вывод:
10 15
Здесь make_multiplier не просто считает что-то и отдаёт число. Она создаёт и возвращает новую функцию multiplier, причём каждый раз разную: одна умножает на 2, другая на 3. Это уже прямой предок декоратора, к которому мы придём через пару уроков. Пока просто зафиксируй факт: функция может рождать другую функцию.
Функции высшего порядка - это не абстрактная теория ради теории. Ты пользуешься ими каждый раз, когда пишешь код на Python, часто даже не задумываясь об этом:
Вывод:
['киви', 'банан', 'вишня', 'яблоко']
sorted принимает параметр key - и в этот параметр можно передать любую функцию. Мы передали встроенную len, и список отсортировался по длине строк. Ещё пример:
Вывод:
[2, 4, 6]
[1, 4, 9, 16, 25, 36]
filter и map - это тоже функции высшего порядка из стандартной библиотеки: обе принимают функцию первым аргументом. Про lambda отдельно: это просто способ написать маленькую безымянную функцию прямо на месте, без def. По сути lambda x: x ** 2 и def square(x): return x ** 2 делают одно и то же, разница только в синтаксисе и в том, что lambda ограничена одним выражением.
Обычно не просят дать определение. Просят что-то вроде: "напиши функцию, которая принимает список чисел и функцию-предикат (функцию, которая проверяет условие и возвращает True или False), и возвращает только те числа, которые предикату удовлетворяют" - то есть написать свой собственный filter. Или: "объясни, чем sorted(data, key=...) отличается от написания собственного цикла сортировки". Смысл вопроса всегда один: понимаешь ли ты, что функция это значение, которое можно передавать, а не привязанная намертво конструкция.
x = func, а не x = func().sorted, filter, map - это функции высшего порядка из стандартной библиотеки, ты пользовался ими и раньше, даже не зная термина.make_multiplier), это прямая дорога к декораторам, о которых пойдёт речь через два урока.В следующем уроке разберём, почему multiplier внутри make_multiplier вообще помнит значение factor уже после того, как make_multiplier закончила работу. Это называется замыканием, и без него декоратор просто не может существовать.