В программировании функциональным стилем очень часто используются выражения-генераторы и выражения-списки.
Основные цели использования этих выражений в функциональном программировании:
Выражения-списки (listcomps) и выражения-генераторы (genexps) в Python, заимствованы из языка функционального программирования Haskell.
Например, с помощью этих выражений можно удалить все пробелы из потока строк с помощью следующего кода:
# список строк >>> line_list = [' \n', ' line 1\n', 'line 2 \n', ' line 3 \n'] # Выражение-генератор - возвращает итератор >>> strip_iter = (line.strip() for line in line_list) >>> strip_iter # <generator object <genexpr> at 0x7f0c30172900> >>> [line for line in strip_iter] # ['', 'line 1', 'line 2', 'line 3'] # выражения-список - возвращает список >>> strip_list = [line.strip() for line in line_list] >>> strip_list # ['', 'line 1', 'line 2', 'line 3']
Также, выражения-генераторы можно использовать, что бы выбрать только определенные элементы (в качестве фильтра). Для этого достаточно добавить условие if
в соответствующее выражение:
>>> line_list = [' \n', ' line 1\n', 'line 2 \n', ' line 3 \n'] # выберем все строки, кроме пустых >>> strip_list = [line.strip() for line in line_list if line.strip() != ""] >>> strip_list # ['line 1', 'line 2', 'line 3']
При использовании выражения-списка на выходе получается обратно список Python. Переменная strip_list
- это список, содержащий результирующие строки, а не итератор. Выражение-генератор, в свою очередь возвращают итератор, который вычисляет значения по мере необходимости, не нуждаясь в материализации всех значений сразу. Это означает, что работая с большими объёмами (потоками) данных выражения-список бесполезен, т.к. он держит весь объем данных в памяти. В таких ситуациях предпочтительны выражения-генераторы.
Обратите внимание, что выражение-генератор заключен в круглые скобки ()
, а выражение-список в квадратные скобки []
. Эти выражения поддерживают бесконечную вложенность. Общий случай выглядит следующим образом:
# на примере выражения генератора ( expression for expr in sequence1 if condition1 for expr2 in sequence2 if condition2 for exprN in sequenceN if conditionN )
Вложенное выражение-список отличается только внешне, необходимо заменить круглые скобки на квадратные.
Элементы сгенерированного вывода будут последовательными значениями expression
. Условия if
являются необязательными, но если присутствует, то expression
вычисляется и добавляется к результату только тогда, когда condition
истинно.
Выражение-генератор должен всегда быть записан в круглых скобках, но круглые скобки, сигнализирующие о вызове функции, также будут учитываться. Такое поведение очень удобно, если необходимо создать итератор, который нужно немедленно передать функции:
Например:
obj_total = sum(obj.count for obj in list_all_objects())
Предложения for...in
содержат последовательности, по которым нужно итерироваться. Последовательности не обязательно должны быть одинаковой длины, так как они повторяются слева направо, а не параллельно. Другими словами, эти вложенные выражения работают эквивалентно следующему коду 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] # [('a', 1), ('a', 2), ('a', 3), # ('b', 1), ('b', 2), ('b', 3), # ('c', 1), ('c', 2), ('c', 3)]
Чтобы избежать двусмысленности в синтаксисе выражений-генераторов и выражений-списков, если выражение expression
создаёт кортеж, то это выражение должно быть заключено в круглые скобки (a, b)
. В примере ниже, первое выражение-список является синтаксически неверным, а второе правильно:
# запись синтаксически неверна [x, y for x in seq1 for y in seq2] # запись верна [(x, y) for x in seq1 for y in seq2]