01 / 72
ТеорияБазовыйJunior-Middle

Функции как объекты первого класса

Частота на собеседованиях: Высокая - фундамент, без которого не понять декораторы и функциональный стильСпрашивают в: Т-Банк, Яндекс, Сбер, Авито

Смотри, вот тебе бытовая аналогия для разгона. Представь обычную отвёртку. Её можно держать в руке и крутить шурупы. А можно положить в ящик с инструментами. Можно передать соседу. Можно даже подписать сверху стикером "моя лучшая отвёртка". Отвёртка при этом остаётся отвёрткой и продолжает делать своё дело, где бы она физически ни находилась.

В Python функция устроена точно так же. Это не какая-то особая магическая конструкция, которая существует только в момент вызова. Это самый обычный объект, такой же как число, строка или список. Его можно положить в переменную, сложить в список, передать в другую функцию как аргумент и даже вернуть из функции как результат.

Вот, собственно, и всё определение того, что называют "функции как объекты первого класса" (first-class functions). Первый класс здесь не про элитность, а про то, что с функцией можно делать всё то же самое, что и с любым другим значением в языке.

Функцию можно положить в переменную

python
Загрузка редактора...

Вывод:

Привет, Мир! <class 'function'>

Обрати внимание: say_hello = greet, без скобок. Скобки после имени функции означают "вызови меня прямо сейчас". Без скобок greet - это просто ссылка на объект-функцию, ту самую отвёртку, которую мы кладём в другую переменную. Именно поэтому в Python возможна такая штука: say_hello и greet указывают на один и тот же объект в памяти, оба вызова эквивалентны.

Функцию можно сложить в список или словарь

python
Загрузка редактора...

Вывод:

greet upper len

Функция здесь ведёт себя как любой другой элемент коллекции. У неё, кстати, есть собственные атрибуты, например __name__ - это тоже просто атрибут объекта, как .append у списка.

Функцию можно передать в другую функцию

Вот это уже интереснее и ведёт нас прямиком к понятию функции высшего порядка (higher-order function). Функция высшего порядка - это функция, которая либо принимает другую функцию как аргумент, либо возвращает функцию как результат (либо и то, и другое сразу).

python
Загрузка редактора...

Вывод:

12

Смотри, что тут произошло. apply_twice ничего не знает заранее про increment. Она просто берёт любую функцию, которую ей передали как аргумент func, и вызывает её два раза подряд. Сначала increment(10) дало 11, потом increment(11) дало 12. Мы могли бы передать туда любую другую функцию с одним аргументом, и apply_twice бы её отработала.

Функцию можно вернуть из функции

python
Загрузка редактора...

Вывод:

10 15

Здесь make_multiplier не просто считает что-то и отдаёт число. Она создаёт и возвращает новую функцию multiplier, причём каждый раз разную: одна умножает на 2, другая на 3. Это уже прямой предок декоратора, к которому мы придём через пару уроков. Пока просто зафиксируй факт: функция может рождать другую функцию.

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

Функции высшего порядка - это не абстрактная теория ради теории. Ты пользуешься ими каждый раз, когда пишешь код на Python, часто даже не задумываясь об этом:

python
Загрузка редактора...

Вывод:

['киви', 'банан', 'вишня', 'яблоко']

sorted принимает параметр key - и в этот параметр можно передать любую функцию. Мы передали встроенную len, и список отсортировался по длине строк. Ещё пример:

python
Загрузка редактора...

Вывод:

[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 закончила работу. Это называется замыканием, и без него декоратор просто не может существовать.