2.30. Включения (comprehensions)

Включение (comprehension) строит новый список, множество, словарь или генератор из существующего итерируемого объекта одним выражением. Это замена распространённому шаблону, когда вы начинаете с пустого контейнера и добавляете элементы в цикле.

2.30.1. Списковые включения

squares = [x * x for x in range(5)]
print(squares)

Вывод:

[0, 1, 4, 9, 16]

Тот же цикл, записанный полностью:

squares = []
for x in range(5):
    squares.append(x * x)

Форма включения – это одно выражение, которое строит список на месте. Здесь нет ни squares = [], ни .append – результатом является само значение включения.

Аннотированное выражение "[f(x) for x in xs if cond]": ведущее выражение -- это результат, конструкция for называет переменную цикла, конструкция if фильтрует, какие элементы сохраняются.

Ведущее выражение производит каждый элемент; конструкция for называет переменную цикла; необязательное if отфильтровывает элементы.

2.30.2. Фильтрация с помощью if

Необязательная конструкция if сохраняет только те элементы, которые соответствуют условию:

evens = [x for x in range(10) if x % 2 == 0]
print(evens)

Вывод:

[0, 2, 4, 6, 8]

Фильтр выполняется перед ведущим выражением – сначала проверяется x % 2 == 0; только подходящие значения попадают в x для вывода.

2.30.3. Включения словарей и множеств

Та же форма работает с литералами словарей и множеств.

Словарное включение содержит пару key: value перед for:

squares = {x: x * x for x in range(5)}
print(squares)

Вывод:

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Включение множества использует фигурные скобки и одно выражение:

unique_lengths = {len(w) for w in ["a", "bb", "c", "bb"]}
print(unique_lengths)

Вывод:

{1, 2}

2.30.4. Генераторные выражения

Круглые скобки производят генераторное выражение вместо списка. Значения вычисляются по одному, по требованию:

total = sum(x * x for x in range(1000))

Список из миллиона элементов никогда не строится. Значения по одному поступают в sum(), которая складывает их и отбрасывает каждое по мере поступления.

Генераторные выражения – это правильный выбор при подаче значений в функцию свёртки (sum(), max(), any(), all()) или любой другой код, потребляющий итератор – они экономят память, которую использовал бы эквивалентный список.

2.30.5. Когда не стоит использовать включение

Включения лаконичны, но не всегда понятнее. Используйте обычный цикл for, когда:

  • Тело требует более одного оператора (во включение помещается ровно одно выражение).

  • Тело имеет побочные эффекты (вывод, запись в файл) – включения предназначены для построения коллекции, а не для выполнения действий.

  • У фильтра или преобразования так много частей, что включение больше не читается слева направо.