Python · Синтаксис · Промежуточный

Включения списков и словарей

10 задач

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

Comprehension для списка, dict и set: синтаксис, фильтрация, генераторы

#
**От цикла к comprehension** Генераторное выражение списка — это однострочное выражение, которое строит список, трансформируя и/или фильтруя итерируемый объект. Оно заменяет распространённый паттерн с циклом: ```python # Традиционный цикл squares = [] for x in range(10): squares.append(x ** 2) # List comprehension — тот же результат 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'] ``` **Comprehension для dict и set** Тот же синтаксис работает для dict и set: ```python # Dict comprehension 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 comprehension — автоматически убирает дубликаты 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 `:=` в comprehension (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` «утекает» в охватывающую область после comprehension — в отличие от обычных переменных comprehension, которые ограничены его областью.

Вложенные comprehension, декартово произведение и производительность

#
**Вложенные comprehension** Вложенный comprehension имеет два цикла `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: # внешний — первый в comprehension for n in row: # внутренний — второй в comprehension 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')] ``` **Comprehension из comprehension** Выражение само может быть comprehension, создавая список списков: ```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]] ``` **Производительность: comprehension vs цикл** List comprehension обычно быстрее эквивалентных `for`-циклов в CPython, потому что байткод для построения списка оптимизирован: ```python import timeit # Comprehension 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)] # почти так же, читабельнее ```

Когда не использовать comprehension: побочные эффекты, вложение, ошибки

#
**Когда НЕ использовать comprehension** Comprehension отлично подходят для простых, декларативных преобразований. Но у них есть предел читаемости — если его превысить, обычный цикл лучше. **Практическое правило**: если не можешь прочитать вслух на одном дыхании — используй цикл. ```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] ``` **Никогда не используй comprehension для побочных эффектов** ```python # ПЛОХО — comprehension только ради побочного эффекта: [print(x) for x in items] # создаёт бесполезный список None [db.save(item) for item in items] # та же проблема # ХОРОШО — используй цикл для побочных эффектов: for x in items: print(x) ``` **Частые ошибки с comprehension** ```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. Comprehension над генератором, который исчерпан: 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 comprehension Большой датасет, обработка по одному генераторное выражение Побочные эффекты (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)