Рубріки: Теория

Функция без имени: что такое lambda в Python и как их использовать

Сергій Бондаренко

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

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

Что такое lambda в Python?

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

Lambda-функция в Python — это тоже функция. Но у нее есть одна особенность — она анонимная, то есть, у нее нет имени.

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

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

Также в анонимной функции может содержаться только одно выражение и могут передаваться сколько угодно аргументов. 

Синтаксис lambda-функции

Синтаксис lambda-функции достаточно простой. Саму функцию определяют с помощью оператора lambda:

lambda arguments: expression

В этой записи:

  • arguments — это аргументы (параметры функции);
  • expression — ее тело, то есть то, что она делает.

Например, простая lambda-функция, которая возвращает переданное ей число, возведенное в куб, будет выглядеть так: lambda x:x**3.

Узнайте больше о том, как и когда применять лямбда-функцию на онлайн курсе Python от Powercode Academy. Освоить новую профессию удастся уже через 4 месяца.

Когда нужно использовать лямбда-функцию?

Lambda-функция была добавлена в язык Python для удобного создания коротких, одноразовых функций.

Если в небольшом участке кода используется какая-то функция и только один раз, имеет смысл применить lambda-функцию. 

Также лямбду удобно применять, если нужно передать функцию в качестве аргумента другой функции, например, в функции map(), filter() или reduce().

Вместо определения функции и передачи ее имени, можно обратиться к lambda-функции, которая определяется непосредственно в аргументах вызова функции.

Для наглядности рассмотрим случай, когда функция вычисляет по формуле теоремы Пифагора значение гипотенузы:

def Gipotenuza (x, y):
    return (x**2+y**2)**(1/2)
print('Гипотенуза равна', Gipotenuza(45,28))

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

Gipotenuza = lambda x, y: (x**2+y**2)**(1/2)
print('Гипотенуза равна', Gipotenuza(45,28))

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

Преимущества и недостатки lambda-функций

Преимущества

  • Краткость и лаконичность. Lambda-функции позволяют создавать функции с минимальным объемом кода. Они могут быть использованы для создания простых функций без необходимости определения имени функции и ключевого слова def.
  • Эффективное использование памяти. Lambda-функции более эффективно используют память, потому что не создают лишних объектов. Обычные функции создают отдельные объекты функций в памяти, в то время как анонимные функции создаются непосредственно перед использованием и удаляются сразу после него.
  • Скорость. Lambda-функции работают быстрее, чем обычные функции, когда они используются для создания функций с небольшим количеством строк кода.

Недостатки анонимных функций

Анонимная функция в основном используется для простых операций. В тех случаях, когда нужно задействовать более сложную логику, имеет смысл написать обычную функцию. Так будет проще; к тому же, ее можно будет переиспользовать в разных частях кода.

Другие недостатки:

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

Lambda-функции используют только переменные, которые передаются им в качестве аргументов.

  • Лямбда-функции не могут содержать документацию в формате docstring. Docstring (докстринг) — это строка документации, которая предназначена для описания функций, методов, классов и модулей в языке программирования Python. Документацию об объекте можно извлечь с помощью интегрированной функции help().

Например, docstring для функции, которая вычисляет факториал числа может выглядеть так: 

def factorial(n):
    """Вычисляет факториал числа n.
    Параметры:
    n (int): число, для которого необходимо вычислить факториал.
    Возвращает:
    int: факториал числа n.
    """

    if n == 0:
        return 1
    else:
        return n * factorial(n-1)

В приведенном выше примере docstring заключен в тройные кавычки и содержит описание функции, а также параметров, которые принимает функция и возвращаемое значение. 

Поскольку лямбда-функции являются выражениями, а не определениями функций, они не могут содержать docstring в том же формате, что и обычные функции.

Чтобы решить эту проблему, можно использовать комментарии, из которых пользователь узнает, что делает данная лямбда-функция.

Допустим, мы имеем дело с lambda-функцией, которая умножает входной аргумент на 2 и добавляет 1. Используем комментарий для описания того, что делает функция:

func = lambda x: x * 2 + 1  # умножаем входной аргумент на 2 и добавляем 1

Конечно, это менее удобно, чем обычный docstring, но так мы можем дать понять другим разработчикам, что именно выполняет лямбда-функция.

Научиться писать не только функционирующий, но и чистый и понятный код на Python можно на курсах от Hillel. Доступны два варианта: Basic курс для новичков и Advanced для тех, кто уже учил язык сам или на других курсах.

Примеры lambda-функции

Рассмотрим ряд примеров lambda-функции на практике.

Создание словаря

Лямбда-функцию можно использовать для создания словаря. Код ниже создает словарь insect_lengths, который содержит длину каждого слова из списка insects:

insects = ['ant', 'bee', 'caterpillar', 'dragonfly']
insect_lengths = {insect: len(insect) for insect in insects}
print(insect_lengths)
# Output: {'ant': 3, 'bee': 3, 'caterpillar': 11, 'dragonfly': 9}

Каждый элемент списка insects перебирается в цикле, и для каждого элемента создается ключ в словаре insect_lengths, значение которого равно длине строки, соответствующей этому ключу. 

Группировка элементов списка по критерию

Вот как можно использовать lambda-функцию для группировки элементов списка по определенному критерию:

from itertools import groupby

fruits = ['highload', 'tomorrow', 'yesterday', 'kitten', 'editor']
grouped_fruits = groupby(fruits, key=lambda x: x[0])
for key, group in grouped_fruits:
    print(key, list(group))

# # Output:
# h ['highload']
# t ['tomorrow']
# y ['yesterday']
# k ['kitten']
# e ['editor']

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

Функция groupby принимает на вход итерируемый объект и ключевую функцию (в данном случае лямбда-функцию). Последняя будет обрабатывать каждый элемент списка для определения критерия группировки — первой буквы каждого элемента. 

Лямбда-функция lambda x: x[0] извлекает первую букву каждого элемента. Результат выполнения функции groupby — объект-итератор, который возвращает пары, состоящие из ключа (в данном случае первой буквы) и группы элементов, удовлетворяющих этому критерию.

С помощью функции print для каждой пары (ключ, группа) выводится ключ и список элементов в группе.

Пример фильтрации данных в базе данных

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

import sqlite3
# Устанавливаем соединение с базой данных
conn = sqlite3.connect('example.db')

# Создаем таблицу
conn.execute('''CREATE TABLE IF NOT EXISTS employees (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL,
                age INTEGER NOT NULL,
                salary REAL NOT NULL)''')

# Записываем данные в таблицу
conn.execute("INSERT INTO employees (id, name, age, salary) VALUES (1, 'Сергей', 25, 50000)")
conn.execute("INSERT INTO employees (id, name, age, salary) VALUES (2, 'Максим', 30, 60000)")
conn.execute("INSERT INTO employees (id, name, age, salary) VALUES (3, 'Оксана', 35, 70000)")
conn.execute("INSERT INTO employees (id, name, age, salary) VALUES (4, 'Виктория', 40, 80000)")
conn.execute("INSERT INTO employees (id, name, age, salary) VALUES (5, 'Варфоломей', 45, 90000)")
conn.execute("INSERT INTO employees (id, name, age, salary) VALUES (6, 'Людмила', 50, 100000)")

# Извлекаем данные из таблицы и фильтруем их с помощью анонимной функции
result = conn.execute("SELECT * FROM employees WHERE age > 30 AND salary > 60000").fetchall()

print(result)

# Закрываем соединение
conn.close()

Мы создаем базу данных SQLite и таблицу employees с несколькими полями. Затем мы вставляем несколько записей в таблицу.

Далее мы используем лямбда-функцию в SQL-запросе, чтобы отфильтровать данные в таблице. Лямбда-функция используется для указания правила отбора: мы оставляем только те записи, где возраст работника составляет более тридцати лет, а зарплата выше $60 000.

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

[(3, 'Оксана', 35, 70000.0), (4, 'Виктория', 40, 80000.0), (5, 'Варфоломей', 45, 90000.0), (6, 'Людмила', 50, 100000.0)]

Лямбда-функции и функции высшего порядка

Лямбда-функции широко используются в функциях высшего порядка, таких как map(), filter() и reduce(), которые позволяют применять функции к последовательностям значений.

Вот пример использования лямбда-функции вместе с map() для преобразования списка строк в список их длин.

Код без лямбды будет выглядеть так:

strings_list = ['Highload', 'Today', 'Ukraine', 'Bondarenko', 'Serhii']
lengths_list = []

for string in strings_list:
    lengths_list.append(len(string))

print(lengths_list)

Добавление лямбды сократит его до трех строчек:

strings_list = ['Highload', 'Today', 'Ukraine', 'Bondarenko', 'Serhii']
lengths_list = list(map(lambda string: len(string), strings_list))

print(lengths_list)

В этом примере мы используем встроенную функцию map(), которая применяет заданную функцию (в данном случае лямбда-функцию, которая возвращает длину строки) ко всем элементам итерируемого объекта (в данном случае списка строк strings_list).

Результатом выполнения функции map() будет объект типа map, который мы приводим к типу списка с помощью функции list().

Еще один пример использования лямбда-функции вместе функциями высшего порядка демонстрирует применение filter() для фильтрации списка чисел:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))

print(even_numbers)  # [2, 4, 6, 8, 10]

В этом примере лямбда-функция принимает один аргумент x и возвращает:

  • True, если x — четное число;
  • False — если x нечетный.

Функция filter() применяет лямбда-функцию к каждому элементу списка numbers и возвращает только те элементы, для которых лямбда-функция вернула True.

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

Фильтрация списка объектов

Предположим, перед нами стоит задача фильтрации списка объектов.

У нас есть список словарей, каждый из которых включает информацию о фильме, включая:

  • название;
  • год выхода на экраны;
  • жанр (комедия, вестерн, детектив и пр).

Мы хотим отфильтровать этот список, чтобы получить только фильмы, выпущенные после 2010 года и относящиеся к жанру комедии:

films = [
    {'title': 'The Grand Budapest Hotel', 'year': 2014, 'genre': 'comedy'},
    {'title': 'Inception', 'year': 2010, 'genre': 'thriller'},
    {'title': 'The Hangover', 'year': 2009, 'genre': 'comedy'},
    {'title': 'La La Land', 'year': 2016, 'genre': 'musical'},
    {'title': 'The Social Network', 'year': 2010, 'genre': 'drama'},
    {'title': 'Interstellar', 'year': 2014, 'genre': 'science fiction'},
    {'title': 'Django Unchained', 'year': 2012, 'genre': 'western'},
    {'title': 'The Dark Knight Rises', 'year': 2012, 'genre': 'action'},
    {'title': 'The Wolf of Wall Street', 'year': 2013, 'genre': 'comedy'},
    {'title': 'Mad Max: Fury Road', 'year': 2015, 'genre': 'action'},
]

comedy_films_after_2010 = list(filter(lambda x: x['genre'] == 'comedy' and x['year'] > 2010, films))

print(comedy_films_after_2010)

На дисплей выводится результат:

[{'title': 'The Grand Budapest Hotel', 'year': 2014, 'genre': 'comedy'}, {'title': 'The Wolf of Wall Street', 'year': 2013, 'genre': 'comedy'}]

С помощью функции filter список films обрабатывается и на выходе мы получаем только те фильмы, которые выпущены после 2010 года и относятся к жанру комедии.

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

Результат работы функции filter преобразуется в список с помощью функции list, а затем выводится на экран с помощью функции print.

Больше примеров использования лямбды с функциями высшего порядка узнайте на курсе Python Developer от академии IT Step.

Лямбда-функции с reduce()

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

Пример использования lambda и reduce():

from functools import reduce

# список чисел, которые нужно перемножить
numbers = [1, 2, 3, 4, 5]

# использование lambda и reduce() для перемножения всех чисел в списке
product = reduce(lambda x, y: x * y, numbers)

print(product) # вывод на экран 120

В этом примере мы импортируем функцию reduce() из модуля functools, затем определяем список чисел numbers. Мы используем lambda-функцию, чтобы определить операцию, которую нужно выполнить на каждом шаге — умножить два числа x и y.

Затем мы передаем эту lambda-функцию и список numbers в reduce(), чтобы получить произведение всех чисел в списке. В конечном итоге мы получаем значение 120, то есть произведение всех чисел в списке [1, 2, 3, 4, 5].

Тот же код, реализованный через обычное определение функции def будет выглядеть так:

def multiply(x, y):
    return x * y

numbers = [1, 2, 3, 4, 5]
product = 1

for num in numbers:
    product = multiply(product, num)
print(product) # Output: 120

Ошибки, возникающие во время работы с lambda-функцией

Поскольку lambda-функции не могут случайно изменять значения глобальных переменных, они более предсказуемы и менее подвержены ошибкам по сравнению с обычными функциями.

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

Например, если передаваемые аргументы имеют неожиданные значения (то есть значения, которые не соответствуют ожидаемому формату или типу данных), то результат работы lambda-функции также может быть неожиданным.

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

# lambda-функция ожидает получить значение суммы чисел
sum = lambda a, b: a + b

# Все работает, если мы передаем числа
print(sum(103, 17)) # Вывод: 120

# Ошибка, если мы передаем не числа
print(sum('Highload', 17))
# Вывод: TypeError: can only concatenate str (not "int") to str

Другой пример — когда лямбда-функция ожидает получить список строк и выводит первый символ каждой строки:

get_first_char = lambda
str_list: [s[0] for s in str_list]

# Работает, если мы передаем список строк
print(get_first_char(['highload', 'today', 'ua']))
# Вывод: ['h', 't', 'u']

Но если мы передаем список, содержащий не только строки, то получаем ошибку:

print(get_first_char(['highload', 103, 'today']))
# Вывод: TypeError: 'int' object is not subscriptable

Приходите на курс Python от Mate Academy, чтобы научиться писать код на Python без ошибок. Обучаться можно бесплатно и оплатить курс уже после трудоустройства.

Альтернативы лямбдам

В Python есть несколько альтернатив использованию lambda-функций.

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

Например, так:

def plus(z, g):
    return z + g

result = plus(4, 25)
print(result) # Output: 29

Во-вторых, можно прибегнуть к функции partial из модуля functools.

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

from functools import partial

def plus(x, y):
    return x + y

add_three = partial(plus, 13)

result = add_three(52)
print(result) # Output: 65

В некоторых случаях выражения-генераторы могут заменить использование лямбда-функций.

Например, у нас есть список чисел от 1 до 10, и мы хотим создать новый список, в котором каждый элемент будет увеличен на 1:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
new_numbers = list(map(lambda x: x + 1, numbers))

Вместо использования функции map() и лямбда-функции, мы применим выражение-генератор [x + 1 for x in numbers], которое создаст новый список, содержащий элементы из списка numbers, увеличенные на 1:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
new_numbers = [x + 1 for x in numbers]

Заключение

Теперь вы знаете, что в Python есть два варианта определения функций:

  • обычные;
  • lambda-функции.

Обычные функции определяются ключевым словом def, а lambda-функции — ключевым словом lambda. Обычные функции могут содержать несколько выражений, а lambda-функции — только одно. Также у lambda-функций нет имен, то есть они анонимные.

Использование lambda-функций в приложении позволяет сэкономить на объеме кода и более эффективно расходовать память. Но при использовании лямбда-функций нужно учитывать, что они могут быть неэффективными при обработке больших объемов данных.

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

В заключение рекомендуем вам посмотреть видео, где рассказывается, зачем нужна лямбда-функция и как она передается в качестве параметра:

А для тех, кто хочет изучить Python глубже и под присмотром опытных менторов, рекомендуем профессиональные курсы. Если вам подходит только гибкий график, можно рассмотреть вечерний формат обучения от Mate Academy.

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023