List comprehensions и другие comprehensions в Python

В Python вы можете превратить циклы for в однострочники с помощью comprehensions (на русский этот термин часто переводится как “генераторы” или “представления”).

Python поддерживает 4 типа comprehensions:

  1. List comprehensions (представления списков)
  2. Dictionary comprehensions (представления словарей)
  3. Set comprehensions (представления множеств)
  4. Generator comprehensions (представления генераторов)

В этой статье мы рассмотрим работу каждого вида comprehensions Python, а также плюсы и минусы их использования.

Примечание редакции. О tuple comprehension можно почитать в статье “Представление кортежей в Python”.

1. List Comprehensions

Представление списка имеет следующий синтаксис:

[expression for var in input_list if condition]

Часть с условием if condition опциональна.

Пример list comprehension

Давайте создадим список чисел, исключив из него все отрицательные числа. Сначала решим эту задачу с использованием цикла for:

numbers = [4, -2, 7, -4, 19]
new_nums = []
for num in numbers:
    if num > 0:
        new_nums.append(num)
print(new_nums)


# Вывод:
# [4, 7, 19]

Но вы можете сделать этот цикл for короче, используя представление списка:

new_nums = [num for num in numbers if num > 0]
print(new_nums)


# Вывод:
# [4, 7, 19]

2. Dictionary Comprehensions

В Python есть возможность сократить код для перебора словарей в цикле. Для этого применяются dict comprehensions или представления словарей.

Синтаксис представления словаря следующий:

{key:value for (key,value) in dict.items() if condition}

Пример dictionary comprehension

Возведем в квадрат все числовые значения словаря, используя представление словаря:

data = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
squared = {k:v*v for (k,v) in data.items()}
print(squared)


# Вывод:
# {'a': 1, 'b': 4, 'c': 9, 'd': 16, 'e': 25}

3. Set Comprehensions

Представление множеств напоминает представление списков. Синтаксис set comprehension следующий:

{expression for var in input if condition}

Пример set comprehension

Создадим список чисел и выберем из него во множество все четные числа:

numbers = [13, 21, 14, 24, 53, 62] 
  
filtered_nums = set() 
  
for num in numbers: 
    if num % 2 == 0: 
        filtered_nums.add(num) 
  
print(filtered_nums)


# Вывод:
# {24, 62, 14}

При помощи set comprehension весь этот код можно вместить в одну строку:

filtered_nums = {num for num in numbers if num % 2 == 0}
print(filtered_nums)


# Вывод:
# {24, 62, 14}

4. Generator Comprehensions

Генераторы поддерживают представления. Но представления генераторов больше известны как генераторные выражения.

Как и другие comprehensions, генераторные выражения предлагают сокращенный синтаксис для цикла for.

Синтаксис генераторного выражения:

(expression for var in input if condition)

Пример генераторного выражения

Возведем в квадрат все четные числа списка и отбросим все нечетные. Для начала можно использовать цикл for:

def square_even(numbers):
    for number in numbers:
        if number % 2 == 0:
            yield(number * number)
            
numbers = [1, 2, 3, 4, 5, 6]
squared_numbers = square_even(numbers)

for number in squared_numbers:
    print(number)

Вывод:

4
16
36

Благодаря генераторному выражению можно вообще забыть о square_even() и сделать все то же самое с помощью одной строки кода:

squared_numbers = (num * num for num in numbers if num % 2 == 0)
for number in squared_numbers: 
    print(number)

Вывод:

4
16
36

Когда не следует использовать представления?

Итак, мы разобрали четыре типа представлений в Python. Может возникнуть соблазн заменить все циклы for в ваших проектах на представления. Однако прежде чем это сделать, давайте обсудим недостатки такого подхода.

Если говорить коротко, представления (списков, словарей, множеств, генераторов) не стоит использовать, если это приводит к снижению качества кода.

Хорошим примером является работа со вложенными циклами for. Если написать вложенный цикл for в виде представления, то можно сэкономить несколько строк, но качество кода может ухудшиться.

Например, матрица часто представляется в Python как список списков. Если вы хотите сгладить матрицу, вы можете использовать представление списка:

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
 
flat_matrix = [num for row in matrix for num in row]
print(flat_matrix)


# Вывод:
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Код лаконичен, но может быть не так интуитивно понятен. Если вместо этого использовать вложенный цикл for, код станет более понятным:

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
 
flat_matrix = []

for row in matrix:
    for num in row:
        flat_matrix.append(num)

print(flat_matrix)


# Вывод:
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Сравнение производительности циклов for и comprehensions

Наконец, давайте посмотрим на производительность представлений. Например, при возведении в квадрат 1M целых чисел, представление списка работает примерно на 10% быстрее, чем цикл for.

Проведение аналогичного замера для представления множеств и словарей дает схожие результаты: comprehension работает лишь немногим лучше цикла.

(Для генераторов сравнение генераторного выражения generator и цикла for + yield бессмысленно: оба возвращают объект-генератор почти мгновенно. На вычисление значений время не тратится, поскольку генераторы не хранят значения).

Вывод: не используйте представления для повышения производительности. Выигрыш незначителен по сравнению с вредом, который приносят длинные и многословные comprehensions. Используйте представления, когда это позволяет очистить код без потери его качества.

Заключение

При помощи представлений циклы for можно превратить в однострочники. Python поддерживает четыре вида представлений для распространенных структур данных:

  • представление списка
  • представление словаря
  • представление множества
  • представление генератора

Разумное использование представлений может улучшить качество вашего кода. Но не используйте их вслепую. Иногда замена вложенного цикла for на comprehension может снизить понятность кода.

Представления могут превзойти цикл for. Но ради улучшения производительности их использовать не стоит, так как выгода не очень велика. Вместо этого отдавайте предпочтение качеству кода.

Спасибо за прочтение. Успешного кодинга!

Перевод статьи Artturi Jalli “Comprehensions in Python—Write Shorter For Loops”.

11 комментариев к “List comprehensions и другие comprehensions в Python”

  1. Пингбэк: Dict comprehension в Python - Python Turbo

  2. Пингбэк: Как получить ключ значения в словаре в Python - Python Turbo

  3. Пингбэк: Представление кортежей в Python - pythonturbo

  4. Пингбэк: Вложенные циклы в Python

  5. Пингбэк: Dict comprehension в Python

  6. Пингбэк: Как разбить число на цифры в Python

  7. Пингбэк: Как найти длину строки в Python

  8. Пингбэк: Как заполнить массив случайными числами в Python - pythonturbo

  9. Пингбэк: Генерация случайных чисел в Python - pythonturbo

  10. Пингбэк: Как преобразовать список в строку в Python - pythonturbo

  11. Пингбэк: Как найти индекс символа в строке в Python - pythonturbo

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *