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

Включення списків і словників

10 завдань

Будуйте списки, словники та множини в одному виразі. Охоплює включення списків, словників, множин та вирази-генератори.

Компрехеншн списку, dict та set: синтаксис, фільтрація, генератори

#
**Від циклу до компрехеншн** Компрехеншн списку — це однорядковий вираз, що будує список шляхом перетворення та/або фільтрації ітерованого об'єкта. Він замінює поширений патерн циклу: ```python # Традиційний цикл squares = [] for x in range(10): squares.append(x ** 2) # Компрехеншн списку — той самий результат squares = [x ** 2 for x in range(10)] ``` Загальна структура: ``` [вираз for елемент in ітерований if умова] ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ що звідки брати необов'язковий виводити фільтр ``` **Фільтрація через if** ```python # Лише парні квадрати even_squares = [x ** 2 for x in range(10) if x % 2 == 0] # [0, 4, 16, 36, 64] # Лише непорожні рядки після strip() words = [' hello ', '', ' ', 'world'] clean = [w.strip() for w in words if w.strip()] # ['hello', 'world'] ``` **Компрехеншн dict та set** Той самий синтаксис працює для dict і set: ```python # Компрехеншн dict word_lengths = {w: len(w) for w in ['hello', 'world', 'python']} # {'hello': 5, 'world': 5, 'python': 6} # Інвертувати dict (обережно: працює лише якщо значення унікальні) original = {'a': 1, 'b': 2, 'c': 3} inverted = {v: k for k, v in original.items()} # {1: 'a', 2: 'b', 3: 'c'} # Компрехеншн set — автоматично видаляє дублікати unique_lengths = {len(w) for w in ['hi', 'hello', 'hey', 'world']} # {2, 5} ``` **Вирази-генератори** Заміни `[...]` на `(...)`, щоб отримати лінивий генератор — значення обчислюються на вимогу, проміжний список не зберігається в пам'яті: ```python # Список — будує весь список заздалегідь total = sum([x ** 2 for x in range(10_000_000)]) # Генератор — обчислює одне значення за раз, набагато менше пам'яті total = sum(x ** 2 for x in range(10_000_000)) # При передачі у функцію зовнішні дужки можна опустити ``` **Оператор walrus `:=` у компрехеншн (Python 3.8+)** Іноді потрібно обчислити проміжне значення як для фільтра, так і для виводу. `:=` (walrus) дозволяє присвоїти всередині виразу: ```python # Без walrus — обчислює strip() двічі: clean = [w.strip() for w in words if w.strip()] # З walrus — обчислює один раз, повторно використовує: clean = [s for w in words if (s := w.strip())] ``` Примітка: змінна `s` «витікає» в охоплюючу область після компрехеншн — на відміну від звичайних змінних компрехеншн, які обмежені його областю.

Вкладені компрехеншн, декартовий добуток та продуктивність vs цикли

#
**Вкладені компрехеншн** Вкладений компрехеншн має два цикли `for`. Зовнішній виконується першим, внутрішній — для кожної ітерації зовнішнього — той самий порядок, що у вкладених `for`: ```python # Розгладити 2D-матрицю matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] flat = [n for row in matrix for n in row] # [1, 2, 3, 4, 5, 6, 7, 8, 9] # Еквівалентний цикл: flat = [] for row in matrix: # зовнішній — перший у компрехеншн for n in row: # внутрішній — другий у компрехеншн flat.append(n) # Декартовий добуток pairs = [(x, y) for x in [1, 2, 3] for y in ['a', 'b']] # [(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')] ``` **Компрехеншн з компрехеншн** Вираз сам може бути компрехеншн, створюючи список списків: ```python # Транспонувати матрицю matrix = [[1, 2, 3], [4, 5, 6]] transposed = [[row[i] for row in matrix] for i in range(3)] # [[1, 4], [2, 5], [3, 6]] ``` **Продуктивність: компрехеншн vs цикл** Компрехеншн списків зазвичай швидший за еквівалентні `for`-цикли у CPython, оскільки байткод для побудови списку оптимізований: ```python import timeit # Компрехеншн timeit.timeit('[x*2 for x in range(1000)]', number=10000) # приблизно 0.5 с # Еквівалентний цикл timeit.timeit( 'result=[]\nfor x in range(1000): result.append(x*2)', number=10000 ) # приблизно 0.8 с — ~60% повільніше ``` Функція `map()` зі звичайною вбудованою (без lambda) є найшвидшою для чистого перетворення, але різниця рідко суттєва поза гарячими циклами: ```python list(map(str, range(1000))) # найшвидше для однієї вбудованої функції [str(x) for x in range(1000)] # майже так само, читабельніше ```

Коли не використовувати компрехеншн: побічні ефекти, вкладення, помилки

#
**Коли НЕ використовувати компрехеншн** Компрехеншн чудові для простих, декларативних перетворень. Але вони мають межу читабельності — якщо перейти її, звичайний цикл кращий. **Практичне правило**: якщо не можеш прочитати вголос на одному диханні — використовуй цикл. ```python # ОК — одне перетворення, один фільтр result = [x * 2 for x in items if x > 0] # На межі — два фільтри, одне перетворення result = [x.strip() for x in lines if x.strip() and not x.startswith('#')] # Занадто складно — використовуй цикл: result = [processed for x in data if (y := expensive(x)) is not None if (processed := transform(y)) > threshold] ``` **Ніколи не використовуй компрехеншн для побічних ефектів** ```python # ПОГАНО — компрехеншн лише для побічного ефекту: [print(x) for x in items] # створює непотрібний список None [db.save(item) for item in items] # та сама проблема # ДОБРЕ — використовуй цикл для побічних ефектів: for x in items: print(x) ``` **Поширені помилки з компрехеншн** ```python # 1. Забувають, що walrus (:=) «витікає» за межі clean = [s for w in words if (s := w.strip())] print(s) # ще визначено тут — може бути несподіванкою # 2. Двохрівневе вкладення — важко читати: result = [[cell*2 for cell in row if cell > 0] for row in matrix if sum(row) > 0] # Набагато зрозуміліше як функція з циклами # 3. Компрехеншн над генератором, який вичерпаний: gen = (x for x in range(5)) a = [x for x in gen] # [0, 1, 2, 3, 4] b = [x for x in gen] # [] — генератор вже вичерпаний! ``` **Короткий посібник з вибору** ``` Ситуація Використовуй ───────────────────────────────── ────────────────────────────── Просте перетворення або фільтрація list/dict/set компрехеншн Великий датасет, обробка по одному вираз-генератор Побічні ефекти (print, save тощо) цикл for Складна логіка / кілька умов цикл for з if/else Два+ рівні вкладення цикли for (читабельність перш за все) ```
01

Парні числа

#

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

def even_numbers(numbers):
    pass


result = even_numbers([1, 2, 3, 4, 5, 6, 7, 8])
print(result)
Рішення
def even_numbers(numbers):
    return [n for n in numbers if n % 2 == 0]


result = even_numbers([1, 2, 3, 4, 5, 6, 7, 8])
print(result)
02

Квадрати чисел

#

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

def squares(numbers):
    pass


result = squares([1, 2, 3, 4, 5])
print(result)
Рішення
def squares(numbers):
    return [n ** 2 for n in numbers]


result = squares([1, 2, 3, 4, 5])
print(result)
03

Довгі слова

#

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

def long_words(words, min_length):
    pass


result = long_words(["cat", "elephant", "dog", "hippopotamus"], 4)
print(result)
Рішення
def long_words(words, min_length):
    return [w for w in words if len(w) > min_length]


result = long_words(["cat", "elephant", "dog", "hippopotamus"], 4)
print(result)
04

Слова у верхньому регістрі

#

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

def to_uppercase(words):
    pass


result = to_uppercase(["hello", "world", "python"])
print(result)
Рішення
def to_uppercase(words):
    return [w.upper() for w in words]


result = to_uppercase(["hello", "world", "python"])
print(result)
05

Довжини слів

#

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

def word_lengths(words):
    pass


result = word_lengths(["apple", "banana", "fig"])
print(result)
Рішення
def word_lengths(words):
    return {w: len(w) for w in words}


result = word_lengths(["apple", "banana", "fig"])
print(result)
06

Розгортання матриці

#

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

def flatten(matrix):
    pass


result = flatten([[1, 2, 3], [4, 5], [6, 7, 8, 9]])
print(result)
Рішення
def flatten(matrix):
    return [item for row in matrix for item in row]


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

Унікальні літери

#

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

def unique_letters(text):
    pass


result = unique_letters("hello world")
print(result)
Рішення
def unique_letters(text):
    return {ch for ch in text if ch != " "}


result = unique_letters("hello world")
print(result)
08

Абсолютні значення

#

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

def abs_values(numbers):
    pass


result = abs_values([3, -1, 4, -1, -5, 9, -2, 6])
print(result)
Рішення
def abs_values(numbers):
    return [n if n >= 0 else -n for n in numbers]


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

Інвертований індекс

#

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

def inverted_index(items):
    pass


result = inverted_index(["apple", "banana", "cherry"])
print(result)
Рішення
def inverted_index(items):
    return {item: idx for idx, item in enumerate(items)}


result = inverted_index(["apple", "banana", "cherry"])
print(result)
10

Сума квадратів через генератор

#

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

def sum_of_positive_squares(numbers):
    pass


result = sum_of_positive_squares([1, -2, 3, -4, 5])
print(result)
Рішення
def sum_of_positive_squares(numbers):
    return sum(n ** 2 for n in numbers if n > 0)


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