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

Функція без імені: що таке 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-запиті, щоб відфільтрувати дані у таблиці. Лямбда-функція використовується для вказівки правила відбору: ми залишаємо лише ті записи, де вік працівника становить понад 30 років, а зарплата вища за $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, збільшені на одиницю:

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.

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

Українські програмісти створили Lağoda QT — гру-головоломку кримськотатарською мовою

Українські програмісти створили безплатну гру-головоломку Lağoda QT.  Кожен рівень — вірш одного з видатних кримськотатарських…

07.05.2024

В Copilot для Microsoft 365 додали українську мову

Корпорація Microsoft оголосила про підтримку української мови у Copilot для Microsoft 365. Українська мова входить…

07.05.2024

Google безплатно навчатиме створювати чат-боти за допомогою Gemini. Потрібно тільки знання Python

Корпорація Google запустила реєстрацію задля участі в безплатній програмі Startup School: Gen AI. Програма безплатна…

07.05.2024

Вакансій і наймів більше, а зарплати — менше: що відбувалося на ринку праці у квітні

В квітні на ринку праці збільшилася кількість вакансій для IT-фахівців. На DOU та Djinni спостерігались…

07.05.2024

І всього лише $300. Китайці представили ноутбук на базі RISC-V для ШІ-девелоперів

Китайський стартап SpacemiT представив MuseBook — ноутбук на базі восьмиядерного процесора K1 RISC-V, орієнтований на…

06.05.2024

Учасники Brave1 створили ШІ-платформу HARVESTER для органів держбезпеки

Учасники Brave1, українська команда MATHESIS, розробила для органів держбезпеки платформу HARVESTER на основі штучного інтелекту.…

06.05.2024