Вирази-генератори та спискові вирази

Дві поширені операції зі значеннями, які повертає ітератор:

  1. Виконання певної операції для кожного елемента.
  2. Вибір підмножини елементів, які відповідають певній умові.

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

Спискові вирази та вирази-генератори (англ. list comprehensions та generator expressions, коротка форма: "listcomps" і "genexps") — це стислий запис для таких операцій, запозичений з функціональної мови програмування Haskell (https://www.haskell.org/). Ви можете видалити всі пробіли з потоку рядків за допомогою наступного коду:

line_list = ['  line 1\n', 'line 2  \n', ' \n', '']

# Вираз-генератор (generator expression) -- повертає ітератор
stripped_iter = (line.strip() for line in line_list)
print(stripped_iter)

# Списковий вираз (list comprehension) -- повертає список
stripped_list = [line.strip() for line in line_list]
print(stripped_list)

Ви можете вибрати лише певні елементи, додавши умову if:

line_list = ['  line 1\n', 'line 2  \n', ' \n', '']

# Виконуємо для усіх елементів, окрім пустих рядків ("")
stripped_list = [line.strip() for line in line_list if line != ""]

print(stripped_list)

Зі списковим виразом ви отримуєте назад список Python; stripped_list — це список, що містить результуючі рядки, а не ітератор. Вирази-генератори повертають ітератор, який обчислює значення за необхідності, не потребуючи матеріалізувати всі значення одразу. Це означає, що спискові вирази не корисні, якщо ви працюєте з ітераторами, що повертають нескінченний потік або дуже велику кількість даних. Вирази-генератори є кращими в таких ситуаціях.

Вирази-генератори оточені круглими дужками (), а спискові вирази — квадратними дужками []. Вирази-генератори мають форму:

( вираз for expr in sequence1
             if condition1
             for expr2 in sequence2
             if condition2
             for expr3 in sequence3
             ...
             if condition3
             for exprN in sequenceN
             if conditionN )

Знову ж таки, для спискового виразу лише зовнішні дужки відрізняються (квадратні дужки замість круглих).

Елементи згенерованого виходу будуть послідовними значеннями виразу. Умови if є необов'язковими; якщо вони присутні, вираз оцінюється і додається до результату лише тоді, коли умова є істинною.

Вирази-генератори завжди повинні бути записані в дужках, але дужки, що сигналізують про виклик функції, також враховуються. Якщо ви хочете створити ітератор, який одразу передається функції, ви можете написати:

obj_total = sum(obj.count for obj in list_all_objects())

Умови for...in містять послідовності, над якими треба ітерувати. Послідовності не повинні бути однієї довжини, оскільки вони ітеруються зліва направо, не паралельно. Для кожного елемента в sequence1, sequence2 ітерується з самого початку. sequence3 потім ітерується для кожної отриманої пари елементів з sequence1 та sequence2.

Іншими словами, спискове включення або вираз-генератор еквівалентний наступному коду Python:

for expr1 in sequence1:
    if not (condition1):
        continue   # Пропустити цей елемент
    for expr2 in sequence2:
        if not (condition2):
            continue   # Пропустити цей елемент
        ...
        for exprN in sequenceN:
            if not (conditionN):
                continue   # Пропустити цей елемент

            # Вивести значення
            # виразу.

Це означає, що коли є кілька умов for...in, але немає умов if, довжина отриманого виходу буде дорівнювати добутку довжин усіх послідовностей. Якщо у вас є два списки довжиною 3, вихідний список міститиме 9 елементів:

seq1 = 'abc'
seq2 = (1, 2, 3)
[(x, y) for x in seq1 for y in seq2]

Щоб уникнути двозначності в граматиці Python, якщо вираз створює кортеж, він повинен бути оточений дужками. Перший списковий вираз нижче є синтаксичною помилкою, тоді як другий правильний:

# Syntax error
[x, y for x in seq1 for y in seq2]

# Correct
[(x, y) for x in seq1 for y in seq2]

Текст на цій сторінці є перекладом "Functional Programming HOWTO", автор: A. M. Kuchling. Інформація про копірайт: History and License.