Функции как значения и lambda

Теория

Функция в Python — такое же значение, как число или строка: её можно положить в переменную, в список или словарь, передать в другую функцию и вернуть из функции. Имя без скобок (get_price) — это сама функция, со скобками (get_price(p)) — результат её вызова. На этой разнице держится много инструментов: sorted, min и max принимают параметр key — функцию, которая для каждого элемента говорит, по чему его сравнивать.

Для коротких одноразовых функций есть lambda — функция без имени, записанная одним выражением: lambda p: p["price"]. Её используют прямо в месте передачи, чаще всего как key. Встроенные map и filter тоже принимают функцию, но в Python их обычно заменяют comprehension — он читается проще.

Наглядно

Таблица 1. Функция как значение на списке товаров products.

Запись Что это Результат
get_price сама функция объект функции
get_price(p) вызов функции цена товара p
sorted(products, key=get_price) передача функции товары по возрастанию цены
sorted(products, key=lambda p: p["price"]) то же через lambda товары по возрастанию цены
max(products, key=lambda p: p["price"]) key у max товар с максимальной ценой (не цена)
sorted(products, key=lambda p: (p["category_id"], -p["price"])) ключ-кортеж по категории, внутри — от дорогих к дешёвым

Таблица 2. lambda и def.

lambda def
Имя нет (анонимная) есть
Тело одно выражение, результат возвращается сам любые инструкции, нужен return
Где уместна короткий key, одноразовая функция всё остальное, особенно логика, которую нужно тестировать

На практике

Задача: сортировать каталог по разным полям, находить самый дешёвый и дорогой товар и выбирать обработчик по статусу заказа.

Шаг 1. Сохраняем функцию в переменной

Функцию можно присвоить другому имени и вызвать через него:

python
def format_price(price):
    return f"{price} ₽"

show = format_price
print(show(1499))
print(type(show))

Результат:

вывод
1499 ₽
<class 'function'>

Шаг 2. Передаём функцию в sorted

key — функция, которая для каждого товара возвращает значение для сравнения:

python
products = [
    {"name": "Клавиатура", "price": 5990, "category_id": 3},
    {"name": "Мышь", "price": 1499, "category_id": 3},
    {"name": "Коврик", "price": 490, "category_id": 5},
]

def get_price(product):
    return product["price"]

cheap_first = sorted(products, key=get_price)
print([p["name"] for p in cheap_first])

Результат:

вывод
['Коврик', 'Мышь', 'Клавиатура']

Передаём get_price без скобок — sorted сам вызовет её для каждого товара.

Шаг 3. Пишем то же через lambda

Для одноразового ключа отдельная функция не нужна:

python
by_price_desc = sorted(products, key=lambda p: p["price"], reverse=True)
print([p["name"] for p in by_price_desc])

Результат:

вывод
['Клавиатура', 'Мышь', 'Коврик']

Шаг 4. Находим товар через min и max

С key функции min и max возвращают элемент, а не значение ключа:

python
cheapest = min(products, key=lambda p: p["price"])
print(cheapest["name"], cheapest["price"])
print(max(p["price"] for p in products))

Результат:

вывод
Коврик 490
5990

Вторая строка — максимум из цен: так получается число, а не товар.

Шаг 5. Сортируем по нескольким полям

Ключ-кортеж сравнивается по первому полю, при равенстве — по второму. Минус перед числом даёт обратный порядок для этого поля:

python
ordered = sorted(products, key=lambda p: (p["category_id"], -p["price"]))
print([(p["category_id"], p["name"]) for p in ordered])

Результат:

вывод
[(3, 'Клавиатура'), (3, 'Мышь'), (5, 'Коврик')]

Шаг 6. Используем map и filter

map применяет функцию к каждому элементу, filter оставляет элементы, для которых функция истинна. Обе возвращают ленивый объект — для списка нужен list():

python
names = map(lambda p: p["name"], products)
print(type(names))
print(list(names))
print(list(filter(lambda p: p["price"] < 2000, products))[0]["name"])
print([p["name"] for p in products if p["price"] < 2000])

Результат:

вывод
<class 'map'>
['Клавиатура', 'Мышь', 'Коврик']
Мышь
['Мышь', 'Коврик']

map вернул не список, а объект map, который выдаёт значения по одному. Последняя строка — то же через comprehension: обычно его и выбирают.

Шаг 7. Выбираем функцию по ключу

Словарь функций — простой способ выбрать обработчик по статусу без длинной цепочки if:

python
def notify_paid(order_id):
    return f"Заказ №{order_id} оплачен"

def notify_shipped(order_id):
    return f"Заказ №{order_id} отправлен"

handlers = {"paid": notify_paid, "shipped": notify_shipped}
print(handlers["shipped"](1042))

Результат:

вывод
Заказ №1042 отправлен

Частые ошибки

  • key=get_price(). Скобки вызывают функцию сразу и без аргумента — TypeError: get_price() missing 1 required positional argument. В key передают функцию без скобок.
  • max по значениям вместо key. max(p["price"] for p in products) возвращает цену, а не товар. Нужен товар — max(products, key=...).
  • map без list(). map/filter возвращают ленивый объект, а не список; повторный обход не даст ничего.
  • Сложная логика в lambda. Если lambda не помещается в строку или нужна в нескольких местах — пишите def с именем.
  • Сортировка по строке-числу. key=lambda p: p["price"] при ценах-строках сортирует посимвольно: "990" после "1200". Приводите тип в ключе: int(p["price"]).

Итог

Функция — значение

  • Имя без скобок — функция, со скобками — вызов.
  • Функции кладут в переменные, списки, словари, передают и возвращают.

key

  • sorted/min/max(коллекция, key=функция) — сравнение по результату функции.
  • min/max с key возвращают элемент; ключ-кортеж — сортировка по нескольким полям; -x — обратный порядок поля.

lambda, map, filter

  • lambda аргументы: выражение — короткая безымянная функция.
  • map/filter ленивые — list(...) для списка; чаще выбирают comprehension.

Вопросы на собеседовании

Что значит «функции — объекты первого класса»?

С функцией можно обращаться как с любым значением: присвоить переменной, положить в коллекцию, передать в другую функцию и вернуть из функции. На этом построены key=, колбэки, обработчики, декораторы.

Чем lambda отличается от обычной функции?

lambda — та же функция, но без имени и только из одного выражения, результат которого возвращается автоматически. В ней нельзя писать инструкции (if как оператор, циклы, присваивания). Для логики, которую нужно читать и тестировать, — def.

Как отсортировать товары по категории, а внутри — по убыванию цены?

Ключ-кортежем с минусом у числового поля: sorted(products, key=lambda p: (p["category_id"], -p["price"])). Для нечисловых полей используют две стабильные сортировки: сначала по второстепенному полю, потом по главному.

Ошибка в тексте

Сообщить об ошибке могут зарегистрированные пользователи — так в сообщениях меньше спама.

Войти, чтобы сообщить