**От цикла к 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 (читаемость прежде всего)
```
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)
Напишите функцию, которая принимает список чисел и возвращает новый список, где положительные числа остаются без изменений, а отрицательные заменяются их абсолютными значениями.
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)
No split tab
Настройки cookies
Мы используем необходимые cookies для работы сайта. С вашего разрешения мы также можем сохранять настройки сайта и использовать аналитические и рекламные cookies, чтобы понимать использование сайта и поддерживать развитие проекта.
* Вы всегда можете изменить свой выбор в настройках сайта.
Выберите категории cookies
Настройки аналитики
Можно отключить аналитику использования платформы. Также можно отправить в Google Analytics запрос на удаление данных об использовании этого сайта, связанных с этим браузером.
Учебный workspace
Учитесь, читая, запуская код и решая задачи.
Практикуйте программирование с пояснениями тем, упражнениями, инструментами browser IDE, проверкой regex и тренировкой печати кода в одном workspace.
Открывайте инструменты во вкладках.Упражнения, IDE-инструменты и тренажеры остаются доступными как вкладки сайта.
Переключайтесь без потери контекста.Переходите между пояснениями, кодом и утилитами, сохраняя свое место.
Используйте sidebar как карту.Левые панели содержат навигацию, настройки, файлы, libraries и управление инструментами.
PythonJavaScriptSQLite
Одна IDE, три практичных режима
Python в браузере.Запускайте небольшие скрипты, пробуйте библиотеки и тренируйте API-запросы без установки.
JavaScript для быстрых экспериментов.Проверяйте код для браузера и сравнивайте идеи рядом с учебными материалами.
SQLite для практики с данными.Открывайте обозреватель базы данных, изучайте таблицы, пишите запросы и учитесь SQL локально.
ТемаIDE
Работайте рядом в split tabs
Держите инструкции перед глазами.Откройте упражнение или справочную страницу рядом с IDE, вместо постоянных переключений.
Сравнивайте инструменты во время обучения.Размещайте проверки regex, пояснения и эксперименты с кодом рядом, когда это нужно для задачи.
Закройте split, когда закончите.Workspace вернётся к одной сфокусированной вкладке, а открытые вкладки сайта останутся доступны.
Тренажер слепой печати кода
Или просто текста
Тренажер рассчитан на физическую клавиатуру.Откройте этот раздел на ноутбуке или компьютере с широким экраном. На телефоне тренировка слепой печати не будет корректной.
Скорость: 0 зн/мин
0 слов/мин
Лучшая скорость (60с): 0 зн/мин
0 слов/мин
Ошибки: 0
Общее время: 0.0 с
Для активации режима слепого набора не подсматривайте на физическую клавиатуру.