Python · Синтаксис · Проміжний

lambda, map, filter, reduce

10 завдань

Функціональні інструменти для перетворення та фільтрації колекцій без явних циклів.

Синтаксис lambda, map() і filter() повертають ітератори, functools.reduce

#
**lambda — анонімні функції** `lambda` — це однорядкова анонімна функція з одним виразом. Це не спрощений `def` — вона призначена для простих однорядкових випадків, де іменування функції не додає цінності: ```python # Версія з def: def double(x): return x * 2 # Версія з lambda — повністю еквівалентна: double = lambda x: x * 2 # lambda з двома аргументами: add = lambda x, y: x + y add(3, 4) # 7 # lambda зі значенням за замовчуванням: greet = lambda name, greeting='Привіт': f'{greeting}, {name}!' greet('Аліса') # 'Привіт, Аліса!' greet('Боб', 'Вітаю') # 'Вітаю, Боб!' ``` Lambda найбільш корисна як вбудований аргумент — наприклад, `key=lambda x: x[1]`. **map() — перетворити кожен елемент** ```python # map повертає об'єкт map — не список! result = map(str, [1, 2, 3]) # <map object at 0x...> list(result) # ['1', '2', '3'] # З lambda: squares = list(map(lambda x: x ** 2, [1, 2, 3, 4])) # [1, 4, 9, 16] # map з двома ітерованими: sums = list(map(lambda a, b: a + b, [1, 2, 3], [10, 20, 30])) # [11, 22, 33] # map вичерпується після першої ітерації — перетвори в список, якщо потрібно повторно: m = map(str, range(5)) first = list(m) # ['0', '1', '2', '3', '4'] second = list(m) # [] — вже вичерпано! ``` **filter() — залишити елементи, що відповідають предикату** ```python # filter також повертає ітератор — загорни у list() для результатів evens = list(filter(lambda x: x % 2 == 0, range(10))) # [0, 2, 4, 6, 8] # filter(None, ...) видаляє хибні значення: data = [0, 1, '', 'hello', None, True, [], [1]] truthy = list(filter(None, data)) # [1, 'hello', True, [1]] ``` **functools.reduce() — звернути послідовність до одного значення** `reduce` НЕ є вбудованою функцією в Python 3 — імпортуй з `functools`: ```python from functools import reduce product = reduce(lambda acc, x: acc * x, [1, 2, 3, 4, 5]) # 120 (1*2*3*4*5) # З початковим значенням (уникає помилок на порожніх послідовностях): total = reduce(lambda acc, x: acc + x, [], 0) # 0 — безпечно, не помилка ```

lambda як key=, модуль operator та іменовані функції vs lambda

#
**lambda як функція key=** Найпоширеніше застосування lambda у Python — як аргумент `key=` для `sorted()`, `max()`, `min()`, `groupby()` тощо: ```python students = [('Alice', 85), ('Bob', 72), ('Carol', 91)] # Сортувати за балом (другий елемент) sorted(students, key=lambda s: s[1]) # [('Bob', 72), ('Alice', 85), ('Carol', 91)] # Найвищий бал max(students, key=lambda s: s[1]) # ('Carol', 91) # Сортувати рядки без врахування регістру sorted(['Banana', 'apple', 'Cherry'], key=lambda s: s.lower()) # ['apple', 'Banana', 'Cherry'] ``` **Модуль operator — швидші альтернативи lambda** Для поширених випадків отримання елемента за індексом або атрибуту об'єкта модуль `operator` надає оптимізовані callable, які працюють швидше за lambda: ```python import operator # operator.itemgetter — те саме, що lambda x: x[key] sorted(students, key=operator.itemgetter(1)) # сортувати за індексом 1 # Працює з кількома ключами (повертає кортеж): records = [('Alice', 'NY', 85), ('Bob', 'LA', 85), ('Carol', 'NY', 91)] sorted(records, key=operator.itemgetter(2, 0)) # за балом, потім за ім'ям # operator.attrgetter — те саме, що lambda x: x.attr from dataclasses import dataclass @dataclass class Student: name: str score: int students = [Student('Alice', 85), Student('Bob', 72)] sorted(students, key=operator.attrgetter('score')) # operator.methodcaller — те саме, що lambda x: x.method() words = ['hello', 'WORLD', 'Python'] sorted(words, key=operator.methodcaller('lower')) # ['hello', 'Python', 'WORLD'] ``` **Іменовані функції vs lambda — коли що краще** ```python # Lambda краща: коротка, вбудована, використовується один раз sorted(data, key=lambda x: x['price'] * (1 - x['discount'])) # Іменована функція краща: складна логіка, повторно використовується def effective_price(item): base = item['price'] discount = item.get('discount', 0) tax = item.get('tax', 0.2) return base * (1 - discount) * (1 + tax) sorted(data, key=effective_price) ``` PEP 8 не рекомендує присвоювати lambda змінній (`double = lambda x: x * 2`) — замість цього використовуй `def`.

map/filter vs компрехеншн, functools.partial та посібник з вибору

#
**map/filter vs компрехеншн — порівняння читабельності** ```python numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # Подвоїти всі парні числа — три способи: # 1. map + filter (вкладено, читається зсередини назовні): result = list(map(lambda x: x * 2, filter(lambda x: x % 2 == 0, numbers))) # 2. Компрехеншн (читається зліва направо, найбільш Pythonic): result = [x * 2 for x in numbers if x % 2 == 0] # 3. Цикл for (найбільш явний): result = [] for x in numbers: if x % 2 == 0: result.append(x * 2) # Всі три дають: [4, 8, 12, 16, 20] ``` Компрехеншн — стандартний стиль Python для перетворення + фільтрації. **Коли map/filter ще має сенс** ```python # map з вбудованою функцією (без lambda) — стисло і швидко: names = list(map(str.upper, ['alice', 'bob', 'carol'])) # ['ALICE', 'BOB', 'CAROL'] # Те саме з іменованою функцією: def to_celsius(f): return (f - 32) * 5 / 9 temps_c = list(map(to_celsius, [32, 68, 212])) # [0.0, 20.0, 100.0] # Конвеєр через кілька операцій (функціональний стиль): pipeline = filter(None, map(str.strip, raw_lines)) ``` **Корисне з functools** ```python from functools import partial, lru_cache # partial — створити спеціалізовану версію функції: import operator double = partial(operator.mul, 2) double(5) # 10 double(21) # 42 # lru_cache — мемоїзувати затратні функції: @lru_cache(maxsize=128) def fibonacci(n): if n < 2: return n return fibonacci(n - 1) + fibonacci(n - 2) fibonacci(100) # миттєво, не експоненційно ``` **Короткий посібник з вибору** ``` Завдання Кращий стиль ────────────────────────────────── ───────────────────────────────── Перетворити + відфільтрувати список Компрехеншн списку Перетворити з іменованою/вбудованою fn map() без lambda Лише фільтрація (без перетворення) filter() або компрехеншн Звернути/накопичити до одного значення reduce() або явний цикл Вбудований ключ сортування lambda або operator.itemgetter Функція ключа для повторного вжитку def (іменована функція) ```
01

Подвоєння через map

#

Напишіть функцію, яка приймає список чисел і повертає новий список, де кожне число подвоєне. Використайте map() з лямбдою.

def double_all(numbers):
    pass


result = double_all([1, 2, 3, 4, 5])
print(result)
Рішення
def double_all(numbers):
    return list(map(lambda n: n * 2, numbers))


result = double_all([1, 2, 3, 4, 5])
print(result)
02

Фільтрація додатніх

#

Напишіть функцію, яка приймає список чисел і повертає лише додатні. Використайте filter() з лямбдою.

def keep_positives(numbers):
    pass


result = keep_positives([3, -1, 4, -1, -5, 9, -2, 6])
print(result)
Рішення
def keep_positives(numbers):
    return list(filter(lambda n: n > 0, numbers))


result = keep_positives([3, -1, 4, -1, -5, 9, -2, 6])
print(result)
03

Сортування за довжиною

#

Напишіть функцію, яка приймає список рядків і повертає їх відсортованими за довжиною, від коротших до довших. Використайте sorted() з лямбдою key.

def sort_by_length(words):
    pass


result = sort_by_length(["banana", "fig", "apple", "kiwi"])
print(result)
Рішення
def sort_by_length(words):
    return sorted(words, key=lambda w: len(w))


result = sort_by_length(["banana", "fig", "apple", "kiwi"])
print(result)
04

Добуток через reduce

#

Напишіть функцію, яка приймає список чисел і повертає їхній добуток (перемножує всі елементи). Використайте reduce() з functools.

from functools import reduce


def product(numbers):
    pass


result = product([1, 2, 3, 4, 5])
print(result)
Рішення
from functools import reduce


def product(numbers):
    return reduce(lambda acc, n: acc * n, numbers)


result = product([1, 2, 3, 4, 5])
print(result)
05

Застосувати знижку

#

Напишіть функцію, яка приймає список цін і відсоток знижки та повертає новий список із застосованою знижкою до кожної ціни. Використайте map() з лямбдою.

def apply_discount(prices, discount_percent):
    pass


result = apply_discount([100, 200, 50, 80], 10)
print(result)
Рішення
def apply_discount(prices, discount_percent):
    return list(map(lambda p: p * (1 - discount_percent / 100), prices))


result = apply_discount([100, 200, 50, 80], 10)
print(result)
06

Фільтр парних, квадрати непарних

#

Напишіть функцію, яка приймає список чисел, видаляє парні і повертає квадрати решти. Поєднайте filter() і map().

def odd_squares(numbers):
    pass


result = odd_squares([1, 2, 3, 4, 5, 6, 7])
print(result)
Рішення
def odd_squares(numbers):
    return list(map(lambda n: n ** 2, filter(lambda n: n % 2 != 0, numbers)))


result = odd_squares([1, 2, 3, 4, 5, 6, 7])
print(result)
07

Сортування об'єктів за атрибутом

#

Напишіть функцію, яка приймає список словників з ключами 'name' і 'score', і повертає список, відсортований за score у спадному порядку.

def sort_by_score(players):
    pass


players = [
    {"name": "Alice", "score": 82},
    {"name": "Bob", "score": 95},
    {"name": "Carol", "score": 71},
]
result = sort_by_score(players)
print(result)
Рішення
def sort_by_score(players):
    return sorted(players, key=lambda p: p["score"], reverse=True)


players = [
    {"name": "Alice", "score": 82},
    {"name": "Bob", "score": 95},
    {"name": "Carol", "score": 71},
]
result = sort_by_score(players)
print(result)
08

Максимум за ключем

#

Напишіть функцію, яка приймає список рядків і повертає найдовший з них. Використайте max() з лямбдою key.

def longest_word(words):
    pass


result = longest_word(["cat", "elephant", "ox", "hippopotamus"])
print(result)
Рішення
def longest_word(words):
    return max(words, key=lambda w: len(w))


result = longest_word(["cat", "elephant", "ox", "hippopotamus"])
print(result)
09

Фільтрація за кількома умовами

#

Напишіть функцію, яка приймає список чисел і повертає лише ті, які є одночасно додатніми і парними.

def positive_evens(numbers):
    pass


result = positive_evens([-4, 3, -2, 8, 0, 6, -7, 10])
print(result)
Рішення
def positive_evens(numbers):
    return list(filter(lambda n: n > 0 and n % 2 == 0, numbers))


result = positive_evens([-4, 3, -2, 8, 0, 6, -7, 10])
print(result)
10

Максимальна префіксна сума через reduce

#

Напишіть функцію, яка приймає список чисел і повертає найбільшу суму, яку можна отримати, додаючи елементи зліва направо (максимальне значення будь-якої префіксної суми). Використайте reduce() з functools.

from functools import reduce


def max_prefix_sum(numbers):
    pass


result = max_prefix_sum([1, -3, 2, 5, -1, 3])
print(result)
Рішення
from functools import reduce


def max_prefix_sum(numbers):
    prefix_sums = []
    reduce(lambda acc, n: (prefix_sums.append(acc + n) or (acc + n)), numbers, 0)
    return max(prefix_sums)

# Або зрозуміліше:
def max_prefix_sum(numbers):
    total = 0
    best = 0
    for n in numbers:
        total += n
        best = max(best, total)
    return best


result = max_prefix_sum([1, -3, 2, 5, -1, 3])
print(result)