Лямбда-функции Python: синтаксис, реальное использование и когда их не следует использовать

  1. Главная
  2. Блог
  3. Разработка программного обеспечения

Разработка программного обеспечения

Лямбда-функции Python: Синтаксис, реальное использование и когда их не следует использовать

Лямбда — это крошечная функция, которую можно написать в одной строке именно там, где она вам нужна. Изучите синтаксис, места, где действительно помогают лямбды, ловушку, в которую попадают опытные разработчики, и когда обычная функция просто лучше.

8 мин чтения
Лямбда-функции Python: синтаксис, реальное использование и когда их не следует использовать

Главное

  • лямбда — это анонимная функция, ограниченная одним выражением.
  • Лучшее его использование — в качестве короткого аргумента, особенно key для sorted, min и max.
  • Понимание списка обычно более понятно, чем map или filter с лямбда-выражением.
  • Если вы назначаете лямбда-выражение имени, вместо этого напишите def.

Вам необходимо отсортировать товары по цене. Написание отдельной именованной функции только для того, чтобы сказать «используйте цену», кажется большой церемонией. Лямбда-функции Python позволяют вам определить этот крошечный фрагмент логики прямо там, где вы его используете.

Лямбды удобны, но ими легко злоупотреблять. В этом руководстве показано, где они действительно помогают, а где затрудняют чтение кода. Мы запускали каждый пример на Python 3.13, и показанный результат получен в результате этого запуска.

Что такое лямбда?

Лямбда — это функция без имени, записанная с ключевым словом lambda:

lambda arguments: expression

Он принимает любое количество аргументов и автоматически возвращает значение одного выражения. Здесь нет ключевого слова return и тела операторов.

square = lambda x: x * x
print(square(4))                     # 16

print((lambda a, b=10: a + b)(5))    # 15

Вторая строка определяет лямбду с аргументом по умолчанию и немедленно вызывает ее. Вы редко будете писать это в реальном коде, но это показывает, что лямбда — это обычный функциональный объект.

лямбда против защиты

Эти два создают функции, которые ведут себя одинаково:

square = lambda x: x * x

def square_def(x):
    return x * x

print(square(4), square_def(4))                 # 16 16
print(square.__name__, square_def.__name__)     # <lambda> square_def

Посмотрите на имена. Каждая лямбда называется просто <lambda>, поэтому сообщения об ошибках и журналы не могут сказать вам, какой из них произошел сбой. Это основная причина, по которой руководства по стилю, такие как PEP 8, рекомендуют использовать def всякий раз, когда вы хотите дать функции имя.

Особенность лямбда защита
Имя Анонимный Именованный
Тело Одно выражение Любое количество утверждений
Возврат Неявный Явный return
Строка документации и подсказки по типам Не поддерживается Поддерживается
Читабельные обратные трассировки Шоу <lambda> Показывает имя
Лучшее для Короткие одноразовые аргументы Все остальное

Сортировка по ключу: лучшее применение

Такие функции, как sorted, min и max, принимают функцию key, которая решает, что сравнивать. Вот где сияют лямбды:

products = [("pen", 10), ("bag", 900), ("book", 250)]

sorted(products, key=lambda p: p[1])
# [('pen', 10), ('book', 250), ('bag', 900)]

max(products, key=lambda p: p[1])
# ('bag', 900)

Для сортировки по нескольким параметрам верните кортеж. Здесь люди сортируются по возрасту: сначала самые старые, а затем по имени:

people = [{"name": "Asha", "age": 31}, {"name": "Ben", "age": 25},
          {"name": "Dev", "age": 31}]

[p["name"] for p in sorted(people, key=lambda p: (-p["age"], p["name"]))]
# ['Asha', 'Dev', 'Ben']

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

names = ["Asha", "ben", "Chitra", "dev"]

sorted(names)                  # ['Asha', 'Chitra', 'ben', 'dev']
sorted(names, key=str.lower)   # ['Asha', 'ben', 'Chitra', 'dev']

По умолчанию все слова, написанные с заглавной буквы, ставятся на первое место. Передача str.lower исправляет это без каких-либо лямбда-выражений. Модуль operator предлагает аналогичные помощники, например itemgetter(1) для lambda p: p[1].

карта и фильтр

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

nums = [1, 2, 3, 4, 5, 6]

list(map(lambda n: n * 10, nums))      # [10, 20, 30, 40, 50, 60]
[n * 10 for n in nums]                 # [10, 20, 30, 40, 50, 60]

list(filter(lambda n: n % 2 == 0, nums))   # [2, 4, 6]
[n for n in nums if n % 2 == 0]            # [2, 4, 6]

Результаты идентичны, и большинство разработчиков Python считают, что понимание легче читать. Обратите также внимание, что map и filter возвращают ленивые итераторы, поэтому вам нужно list(), чтобы увидеть значения. Наше руководство по пониманию списков в Python подробно описывает этот стиль. map по-прежнему хорошо читается с существующей именованной функцией, как в map(str.strip, lines).

уменьшить

functools.reduce объединяет элементы в одно значение, несущее текущий результат:

from functools import reduce

reduce(lambda acc, n: acc + n, nums, 0)   # 21
sum(nums)                                 # 21

Для обычных заданий, таких как итоговые значения, используйте встроенные функции, например sum, max или any. Сохраните reduce для более редких случаев и рассмотрите простой цикл, если лямбда начинает выглядеть сложной.

Условия в лямбде

Вы не можете использовать оператор if в лямбде, но можете использовать условное выражение:

label = lambda total: "large" if total >= 1000 else "small"

label(1200), label(300)
# ('large', 'small')

Одно условие — это хорошо. Как только вы начнете их вкладывать, читателям придется потрудиться, что является явным признаком перехода к def ​​с обычными if операторами.

Лямбды, возвращающие функции

Функция может создавать и возвращать лямбду, которая запоминает значение из своего окружения:

def multiplier(n):
    return lambda x: x * n

double = multiplier(2)
double(7)   # 14

Это работает благодаря замыканиям, которые в нашей статье о замыканиях JavaScript объясняются на другом языке. Python functools.partial предлагает другой способ предварительного заполнения аргументов.

Ловушка позднего связывания

В конечном итоге это затрагивает почти всех. Мы создали три лямбды в цикле, ожидая, что каждая вернет свой номер:

funcs = [lambda: i for i in range(3)]
[f() for f in funcs]

# [2, 2, 2]

Все три возвращают 2. Лямбда не фиксирует значение из i при создании. Он ищет переменную, когда она вызывается, и к тому времени цикл завершается, оставляя i со значением 2. Обычное решение состоит в том, чтобы захватить значение в качестве аргумента по умолчанию, поскольку значения по умолчанию оцениваются во время определения:

funcs = [lambda i=i: i for i in range(3)]
[f() for f in funcs]

# [0, 1, 2]

Это не уникально для лямбда-выражений, поскольку def внутри цикла ведет себя точно так же. Просто это чаще встречается с лямбда-выражениями, потому что они обычно создаются в циклах, например, при присоединении обратных вызовов кнопок.

Совет. Если вам нужна функция для каждого элемента, подумайте, нужно ли вам вообще много функций. Часто одна функция, которая принимает элемент в качестве аргумента, более понятна и полностью позволяет избежать ловушки.

Таблицы поиска небольших функций

Лямбды аккуратно вписываются в словарь, который сопоставляет имена небольшим операциям:

ops = {
    "add": lambda a, b: a + b,
    "sub": lambda a, b: a - b,
}

ops["sub"](10, 3)   # 7

Это может заменить длинную цепочку из if и elif. Руководство Словарь Python охватывает больше шаблонов словарей, а модуль operator уже предоставляет operator.add и operator.sub.

Пределы лямбд

  • Только одно выражение. Никаких присваиваний с =, циклов и блоков try.
  • Нет утверждений. Запись lambda x: return x с SyntaxError не удалась, как подтвердил наш тест.
  • Нет строки документации или подсказок по типам. Инструменты и читатели получают меньше помощи.
  • Усложнение отладки. Ошибки указывают на <lambda>, а не на полезное имя.

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

Когда не следует использовать лямбду

Ситуация Лучший выбор
Присвоение его имени защита
Для логики требуется более одной строки защита
Преобразование или фильтрация списка Понимание списка
Суммируем или находим максимум sum, max, min
Получение предмета или атрибута operator.itemgetter или attrgetter
Одна и та же лямбда появляется в нескольких местах Именованная функция, которую вы можете протестировать

Последний пункт имеет значение. Логику, достойную повторного использования, стоит тестировать, а именованную функцию тестировать гораздо проще, как показывает наше руководство по pytest.

Итоги

Хорошая лямбда достаточно коротка, чтобы ее можно было прочитать с первого взгляда, и используется один раз, обычно в качестве аргумента. Сортировка ключей — классический пример. Чтобы проверить свой собственный код, найдите = lambda и превратите его в именованные функции. Затем посмотрите на любой map или filter с лямбда-выражением и посмотрите, будет ли понимание читаться лучше.

Часто задаваемые вопросы

Что такое лямбда-функция в Python?

Это небольшая анонимная функция, созданная с помощью ключевого слова лямбда. Он принимает аргументы и возвращает значение одного выражения без имени или оператора возврата.

Может ли лямбда Python иметь несколько строк?

Нет. Лямбда ограничена одним выражением. Вы можете разделить его на строки внутри скобок, но он по-прежнему не может содержать операторы, поэтому используйте def для более сложной логики.

Могу ли я использовать if else в лямбде?

Вы можете использовать условное выражение, например лямбда x: «большой», если x > 10, иначе «маленький». Оператор if с блоком не допускается.

Являются ли лямбды быстрее обычных функций?

Не осмысленно. Оба создают одинаковый объект функции. Выбирайте между ними по удобочитаемости, а не по производительности.

Должен ли я использовать карту с лямбда-выражением или пониманием списка?

Большинство разработчиков Python предпочитают понимание списка для удобства чтения. Карта разумна, если у вас уже есть именованная функция, например карта(str.strip,lines).

Продолжайте учиться

Работайте с Stack Quora

Нужна помощь с тестированием, WordPress или SEO?

Мы помогаем командам выпускать лучшее программное обеспечение и развивать свои сайты. Расскажите нам, над чем вы работаете, и мы предложим четкий следующий шаг.

Продолжайте читать

Больше руководств по тестирование, искусственный интеллект и современные разработки

Понравилась эта статья? Выберите тему и сразу приступайте к нашим новейшим руководствам.

Искусственный интеллект

Объяснение галлюцинаций искусственного интеллекта: почему языковые модели все выдумывают и как их уменьшить

Галлюцинации ИИ объяснили: что это такое, почему языковые модели их создают, распространенные типы, как уменьшить их с помощью заземления и подсказо…

Читать статью →
DevOps

Объяснение переменных среды: файлы .env, секреты и правильно выполненная конфигурация

Переменные среды объясняются на примерах Python, которые мы запускали: их чтение и установка, почему каждое значение является строкой, файлы .env, н…

Читать статью →
Разработка ПО

Объяснение ограничения скорости API: алгоритмы, заголовки и обработка ошибок 429

Ограничение скорости API объясняется с помощью проведенного нами моделирования: сегмент токенов, фиксированное окно, скользящее окно, 429 ответов, з…

Читать статью →
Автоматизация тестирования

Издевательство в Python: unittest.mock, патч и ошибки, из-за которых тесты лгут

Издевательство на Python с помощью unittest.mock, протестировано: Mock и return_value, Side_effect, исправление использования имени, спецификация и …

Читать статью →
Тестирование ПО

Объяснение покрытия кода: покрытие линий или ветвей и что на самом деле означает 100%

Покрытие кода объяснено с помощью реального запуска покрытия.py: покрытие строк, ветвей и условий, чтение отчета, почему 100%-ное покрытие все еще м…

Читать статью →
Базы данных и SQL

DELETE, TRUNCATE и DROP в SQL: различия, откат и когда использовать каждый из них

Объяснение DELETE, TRUNCATE и DROP на примере протестированного SQL: что каждый из них удаляет, откат, триггеры, сброс идентификаторов, скорость и ч…

Читать статью →
Базы данных и SQL

Объяснение представлений SQL: создавайте, используйте и избегайте ловушек, с примерами

Представления SQL, объясненные с помощью запросов, которые мы выполняли: создание представления, результаты в реальном времени, скрытие столбцов, пр…

Читать статью →
Разработка ПО

Интерфейс TypeScript и тип: реальные различия, протестированные с помощью компилятора

Интерфейс TypeScript против типа, проверенный реальным компилятором: что могут делать оба, что может делать только один, слияние объявлений, расшире…

Читать статью →
Разработка ПО

Объяснение Python *args и **kwargs: гибкие функции с понятными примерами

Python *args и **kwargs, объясненные с помощью кода, который мы запускали: упаковка и распаковка, порядок параметров, аргументы только по ключевым с…

Читать статью →
Разработка ПО

Лямбда-функции Python: синтаксис, реальное использование и когда их не следует использовать

Лямбда-функции Python, объясненные с помощью кода, который мы запускали: синтаксис, сортировка с помощью ключа, карты и фильтра, замыкания, ловушка …

Читать статью →
Искусственный интеллект

Что такое ИИ-агенты? Как они работают: можно запустить простой цикл агента

Что такое агенты ИИ? Простое руководство по циклу агента, инструментам, памяти и ограничениям, с небольшим циклом Python, который мы запустили, реал…

Читать статью →
DevOps

Мониторинг и наблюдаемость: реальная разница с журналами, метриками и трассировками

Объяснение мониторинга и наблюдаемости: известные и неизвестные сбои, журналы, метрики и трассировки, пошаговое руководство по инцидентам при оформл…

Читать статью →
DevOps

Docker Volumes против Bind Mounts: правильное сохранение данных контейнера

Объяснение томов Docker и монтирования привязки: почему исчезают данные контейнера, именованные тома, монтирование привязки, tmpfs, примеры создания…

Читать статью →
Автоматизация тестирования

TDD против BDD: ключевые различия, одна функция, созданная для обоих способов, и когда использовать каждый

TDD и BDD объясняются с помощью одной функции скидки, созданной в обоих направлениях: красно-зеленый рефакторинг с кодом, который мы запускали, сцен…

Читать статью →
Разработка ПО

Git merge vs rebase: отличия, примеры и когда что использовать

Git merge vs rebase с реальным выводом log, сравнительной таблицей, золотым правилом rebase и безопасным рабочим процессом для команды.

Читать статью →
Разработка ПО

Генераторы списков в Python: синтаксис, примеры и когда их избегать

Генераторы списков в Python: синтаксис, фильтры, if-else, вложенные циклы, измеренное время и случаи, когда обычный цикл for лучше.

Читать статью →
Базы данных и SQL

SQL JOIN: объяснение с примерами (Inner, Left, Right, Full)

Разберите SQL JOIN шаг за шагом на примерах таблиц и запросов: INNER, LEFT, RIGHT, FULL, CROSS и SELF, а также частые ошибки и советы по производите…

Читать статью →