Введение

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

Примеры

# списковое включение, выдаёт [2, 3, 4]
[x + 1 for x in (1, 2, 3)]

# генераторное выражение, выдаст 2, затем 3, затем 4
(x + 1 for x in (1, 2, 3)) 

# списковое включение с фильтром выдаёт [2]
[x for x in (1, 2, 3) if x % 2 == 0]

# списковое включение с тройкой
[x + 1 if x % 2 == 0 else x for x in (1, 2, 3)]

# списковое включение с тройкой и фильтрацией
[x + 1 if x % 2 == 0 else x for x in range(-3,4) if x > 0]

# комплект выражений, выдаёт {1, 2, 3}
{x for x in (1, 2, 2, 3)} 

# словарь включений, выдаёт {'a': 1, 'b': 2} (python 2.7+ and 3.0+ only)
{k: v for k, v in [('a', 1), ('b', 2)]} 

# Вложенные циклы, дает [11, 21, 12, 22]
[x + y for x in [1, 2] for y in [10, 20]] 

# Состояние проверено на 1-й петле
[x + y for x in [1, 2, 3] if x > 2 for y in [3, 4, 5]]

# Состояние проверено на 2-й петле
[x + y for x in [1, 2, 3] for y in [3, 4, 5] if x > 2] 

# Условие проверено, если зацикленные числа нечётные
[x for x in xrange(10) if x % 2 == 0] 	

Замечания

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

  • в правой части присвоений
  • в качестве аргументов для вызова функций
  • в теле лямбда-функции
  • как отдельный оператор. (Например: [print(x) для x в диапазоне (10)])

Примеры списковых включений


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

Списковое включение создает новый list, применяя выражение к каждому элементу итерируемого. Наиболее простой формой является:

[ <expression> for <element> in <iterable> ]

Также есть необязательное условие if:

[ <expression> for <element> in <iterable> if <condition> ]

 

Каждый <element> в <iterable> подключается к <expression> если (необязательно) <условие> имеет значение true . Все результаты сразу возвращаются в новый список. Генератор включений вычисляет медленно, а списковые включения оценивают весь итератор — занимая память, пропорционально длине итератора.

Чтобы создать список квадратов целых чисел:

squares = [x * x for x in (1, 2, 3, 4)]
squares #квадраты

>>>Out: [1, 4, 9, 16]

Выражение for  устанавливает x для каждого значения по очереди из (1, 2, 3, 4). Результат выражения x * x добавляется во внутренний список. Внутренний список присваивается переменным квадратам после завершения.

Помимо ускорения (как описано здесь), списковые включения примерно эквивалентны следующему циклу for:

squares = []
for x in (1, 2, 3, 4):
    squares.append(x * x)

>>>Out:[1, 4, 9, 16]

Выражение, применяемое к каждому элементу, может быть настолько сложным, насколько это необходимо:

# Получить список заглавных символов из строки
[s.upper() for s in "Hello World"]

>>>Out:['H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D']

# Убрать все запятые с конца строки в списке
[w.strip(',') for w in ['these,', 'words,,', 'mostly', 'have,commas,']]

>>>Out:['these', 'words', 'mostly', 'have,commas']

# Организовать буквы в словах в алфавитном порядке
sentence = "Beautiful is better than ugly"
["".join(sorted(word, key = lambda x: x.lower())) for word in sentence.split()]

>>>Out:['aBefiltuu', 'is', 'beertt', 'ahnt', 'gluy'] 

Условие Else

else можно использовать в списковых включениях, но нужно следить за синтаксисом. Условие if или else следует использовать перед циклом for, а не после:

# создать список символов из apple, replacing согласные на '*'
# Ex - 'apple' --> ['a', '*', '*', '*' ,'e']
[x for x in 'apple' if x in 'aeiou' else '*']

>>>Out: SyntaxError: invalid syntax

# При использовании if / else используйте их перед циклом
[x if x in 'aeiou' else '*' for x in 'apple']

>>>Out:['a', '*', '*', '*', 'e']

 

Обратите внимание, что здесь используется другая языковая конструкция, условное выражение, которое само по себе не является частью синтаксиса включения. Учитывая, что if после  for…in является частью спискового включения и используется для фильтрации элементов из исходного кода.

Двойная итерация

Порядок двойной итерации [... for x in ... for y in ...] является природным или контр-логичным. Эмпирическое правило это следовать циклу for:

def foo(i):
    return i, i + 0.5

for i in range(3):
    for x in foo(i):
        yield str(x)

 

Это становится:

[str(x)
    for i in range(3)
        for x in foo(i)
]

 

Это может быть сжат в одну строку, как [str(x) for i in range(3) for x in foo(i)]

Встроенная мутация и другие побочные эффекты

Прежде чем использовать списковые включения, нужно понять разницу между функциями, вызываемыми для их побочных эффектов (мутирующие или встроенные функции), которые обычно возвращают None , и функции , которые возвращают интересное значение.

Многие функции (особенно чистые функции) просто берут объект и возвращают какой-то объект. Встроенная функция изменяет существующий объект, это называется побочным эффектом. Другие примеры включают операции ввода и вывода, такие как print.

list.sort() сортирует список на месте (это означает , что он изменяет исходный список) и возвращает значение None . Следовательно, это не будет работать так, как со списковыми включениями:

[x.sort() for x in [[2, 1], [4, 3], [0, 1]]]
>>>Out: [None, None, None]

 

Вместо sorted() возвращает отсортированный list , а не сортировку на месте:

[sorted(x) for x in [[2, 1], [4, 3], [0, 1]]]
>>>Out: [[1, 2], [3, 4], [0, 1]]

 

Допускается использование включений для побочных эффектов, таких как ввод-вывод или встроенных функций. Тем не менее лучше использовать цикл for. Так это работает в Python 3:

[print(x) for x in (1, 2, 3)] 

Вместо этого используйте:

for x in (1, 2, 3):
    print(x)

 

В некоторых ситуациях функции побочных эффектов подходят для списковых включений. У random.randrange() есть побочный эффект изменения состояния генератора случайных чисел, но он также возвращает интересное значение. Кроме того, next() может вызываться на итераторе.

Следующий генератор случайных значений не является чистым, но имеет смысл, поскольку генератор случайных значений сбрасывается каждый раз, когда вычисляется выражение:

from random import randrange
[randrange(1, 7) for _ in range(10)]

>>>Out:[2, 3, 2, 1, 1, 5, 2, 4, 3, 5]

Пробелы в списках

Более сложные списки могут быть очень длинными или становиться менее читабельными. В приведённых примерах это встречается не так часто, но можно разбить списковые включения на несколько строк следующим образом:

[
    x for x
    in 'foo'
    if x not in 'bar'
]

 

Избегайте повторяющихся и тяжёлых операций с использованием условий

Пример

Рассмотрим следующее списковое включение:

def f(x):
    import time
    time.sleep(.1)       # Simulate expensive function
    return x**2

[f(x) for x in range(1000) if f(x) > 10]
>>>Out: [16, 25, 36, ...]

Это приводит к двум вызовам f(x) для 1000 значений x: один вызов для генерации значения, а другой для проверки условия if. Если f(x) является особенно тяжёлой операцией, это может существенно повлиять на производительность. Но хуже всего, если вызов f() имеет побочные эффекты, он может привести к неожиданным результатам.

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

[v for v in (f(x) for x in range(1000)) if v > 10]

>>>Out:[16, 25, 36, ...]

Или, используя эквивалентный map:

[v for v in map(f, range(1000)) if v > 10]

>>>Out:[16, 25, 36, ...]

Другой способ, который может сделать код более читабельным — поместить частичный результат (v в предыдущем примере) в итерацию (например, список или кортеж), а затем выполнить итерацию по нему. Поскольку v будет единственным элементом в итерации, в результате мы имеем ссылку на вывод нашей медленной функции, вычисляемой только один раз:

[v for x in range(1000) for v in [f(x)] if v > 10]

>>>Out:[16, 25, 36, ...]


Однако на практике логика кода может быть более сложной, и важно, чтобы код был читабельным. Как правило, лучше использовать отдельную функцию генератора вместо сложной однострочной:

def process_prime_numbers(iterable):
	for x in iterable:
    	if is_prime(x):
        	yield f(x)

[x for x in process_prime_numbers(range(1000)) if x > 10]
>>>Out: [11, 13, 17, 19, ...]

Другой способ предотвратить многократное вычисление f(x) — это использовать декоратор @functools.lru_cache() (Python 3.2+) для f(x). Поскольку вывод f для ввода x уже был вычислен один раз, второй вызов функции исходного спискового включения будет таким же быстрым, как поиск по словарю. Этот подход использует запоминание для повышения эффективности, что сравнимо с использованием выражений генератора.

Скажем, вы должны сгладить список

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

Одним из методов может быть:

import functools # импорт библиотеки functools для функции reduce() 
functools.reduce(lambda x, y: x+y, l)
>>>Out:[1, 2, 3, 4, 5, 6, 7, 8, 9]

Ярлыки, основанные на + (включая подразумеваемое использование в сумме), по необходимости, O(L^2), где L - подсписки — как промежуточный результат продолжает увеличиваться, на каждом шаге новый объект списка промежуточных результатов резервируется, и все элементы в предыдущем промежуточном результате должны быть скопированы (а также несколько новых добавлены в конце). Поэтому для простоты скажем, что у вас есть L-подсписков с I элементов в каждом: первые I элементы копируются L-1 раз, вторые I-элементы L-2 раза и т.д .; общее количество копий равно I, умноженному на сумму x для x от 1 до L, т.е. I*(L**2)/2.

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

Изменение типов в списке

Пример

Количественные данные часто считываются в виде строк, которые должны быть преобразованы в числовые типы перед обработкой. Типы всех элементов списка могут быть преобразованы с помощью функции списковых включений или функции map().

# Преобразовать список строк в целые числа.
items = ["1","2","3","4"]
[int(item) for item in items]

>>>Out: [1, 2, 3, 4]

# Преобразовать список строк в плавающий.
items = ['1','2','3','4']
list(map(float, items))

>>>Out:[1.0, 2.0, 3.0, 4.0] 


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

Пример

Условие for списковых включений может указывать более одной переменной:

[x + y for x, y in [(1, 2), (3, 4), (5, 6)]]
>>>Out: [3, 7, 11]

[x + y for x, y in zip([1, 3, 5], [2, 4, 6])]
>>>Out: [3, 7, 11]

Это как обычный цикл for:

for x, y in [(1,2), (3,4), (5,6)]:
	print(x+y)

>>>Out:3
>>>Out:7
>>>Out:11


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

[x, y for x, y in [(1, 2), (3, 4), (5, 6)]]

>>>Out: SyntaxError: invalid syntax

[(x, y) for x, y in [(1, 2), (3, 4), (5, 6)]]

>>>Out: [(1, 2), (3, 4), (5, 6)]

Подсчет вхождений с использованием включений

Пример

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

# Посчитать числа в `range(1000)` которые чётные или содержат цифру `9`:

print(sum(
	1 for x in range(1000)
	if x % 2 == 0 and
	'9' in str(x)
))
    
>>>Out: 95

Основная концепция такова:

  1. Перебирать элементы в диапазоне (1000).
  2. Объедините все необходимые if условия.
  3. Используйте 1 в качестве выражения, чтобы вернуть 1 для каждого элемента, который соответствует условиям.
  4. Суммируйте все 1, чтобы определить количество предметов, которые соответствуют условиям.

Примечание: здесь мы не собираем 1 в списке (обратите внимание на отсутствие квадратных скобок), но мы передаём их непосредственно функции суммирования, которая их суммирует. Это генератор выражений, который похож на включения.

Словарь включений

Пример

Словарь включений аналогичен списковым включениям, за исключением того, что он создаёт объект словаря вместо списка.

Основной пример:

{x: x * x for x in (1, 2, 3, 4)}

>>>Out: {1: 1, 2: 4, 3: 9, 4: 16}

 

это просто еще один способ написания:

dict((x, x * x) for x in (1, 2, 3, 4))

>>>Out: {1: 1, 2: 4, 3: 9, 4: 16} 

Как и в случае со списком, мы можем использовать условный оператор внутри словаря включения, чтобы получить только элементы словаря, удовлетворяющие заданному критерию.

{name: len(name) for name in ('Stack', 'Overflow', 'Exchange') if len(name) > 6}  

>>>Out: {'Exchange': 8, 'Overflow': 8} 

Или переписать с помощью генераторного выражения.

dict((name, len(name)) for name in ('Stack', 'Overflow', 'Exchange') if len(name) > 6)
>>>Out: {'Exchange': 8, 'Overflow': 8} 

Начиная со словаря и используя словарь в качестве фильтра пары ключ-значение

initial_dict = {'x': 1, 'y': 2}
{key: value for key, value in initial_dict.items() if key == 'x'}

>>>Out: {'x': 1}

Переключение ключа и значения словаря (инвертировать словарь)

my_dict = {1: 'a', 2: 'b', 3: 'c'}

если вы хотели поменять местами ключи и значения, вы можете использовать несколько подходов в зависимости от вашего стиля кодирования:

swapped = {v: k for k, v in my_dict.items()}
swapped = dict((v, k) for k, v in my_dict.items())
swapped = dict(zip(my_dict.values(), my_dict))
swapped = dict(zip(my_dict.values(), my_dict.keys()))
swapped = dict(map(reversed, my_dict.items()))

print(swapped)

>>>Out: {a: 1, b: 2, c: 3}

Объединение словарей


Объедините словари и при необходимости переопределите старые значения с помощью вложенного словаря включений.

dict1 = {'w': 1, 'x': 1}
dict2 = {'x': 2, 'y': 2, 'z': 2}

{k: v for d in [dict1, dict2] for k, v in d.items()}
>>>Out: {'w': 1, 'x': 2, 'y': 2, 'z': 2}

Примечание: словарь включений был добавлен в Python 3.0 и перенесён в версию 2.7+, в отличие от списочных включений, которые были добавлены в 2.0. Версии <2.7 могут использовать генераторное выражение и встроенную функцию dict() для имитации поведения словаря включений.

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

Пример

Генераторное выражение очень похоже на списки. Основное отличие состоит в том, что оно не создаёт полный набор результатов сразу; он создаёт объект генератора, который затем может быть повторён.

Например, посмотрите различия в коде:

# списковое включение
[x**2 for x in range(10)]

>>>Out: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Примечание: В Python 3, range просто возвращает генератор. Для получения дополнительной информации см Различия между функциями дальности и xrange например.

Сценарии использования


Генераторные выражения вычисляются лениво, что означает, что они генерируют и возвращают каждое значение только тогда, когда генератор повторяется. Это может пригодиться при переборе больших наборов данных, без создания дубликатов набора данных в памяти:

for square in (x**2 for x in range(1000000)):
    #сделает что-нибудь
 

Другим распространенным вариантом использования является недопущение итерации по всей итерации когда не требуется. В этом примере элемент извлекается из удаленного API с каждой итерацией get_objects(). Тысячи объектов могут существовать, должны быть найдены один за другим, и нам нужно только знать, существует ли объект, который соответствует шаблону. Используйте генераторное выражение, когда мы видим похожую задачу

def get_objects():
    # Получает объект из API один за одним
    while True:
        yield get_next_item()

def object_matches_pattern(obj):
    # выполнит потенциально сложный расчет
    return matches_pattern

def right_item_exists():
    items = (object_matched_pattern(each) for each in get_objects())
    for item in items:
        if item.is_the_right_one:


            return True
    return False


 

Набор включений

Пример

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

# Набор, содержащий каждое значение в диапазоне (5):
{x for x in range(5)}

>>>Out: {0, 1, 2, 3, 4}

# Набор чётных чисел от 1 до 10:
{x for x in range(1, 11) if x % 2 == 0}
>>>Out: {2, 4, 6, 8, 10}

# Уникальные буквенные символы в текстовой строке:
text = "When in the Course of human events it becomes necessary for one people..."

{ch.lower() for ch in text if ch.isalpha()}
>>>Out: {'a',
 'b',
 'c',
 'e',
 'f',
 'h',
 'i',
 'l',
 'm',
 'n',
 'o',
 'p',
 'r',
 's',
 't',
 'u',
 'v',
 'w',
 'y'}

Пример

Имейте в виду, что наборы неупорядочены. Это значит, что порядок результатов в наборе может отличаться от того, который представлен в приведённых выше примерах.

Примечание: Набор включений доступен с версии Python 2.7+, в отличие от списка включений, которые были добавлены в 2.0. В Python 2.2 - Python 2.6 функция set() может использоваться с выражением генератора для получения того же результата:

set(x for x in range(5))
>>>Out: {0, 1, 2, 3, 4}

Условные списки

Учитывая список понимание вы можете добавить один или несколько , if условия для фильтрации значений.

[<expression> for <element> in <iterable> if <condition>]

 

Для каждого <element> в <iterable>  если <condition> имеет значение True , добавить <expression> (обычно функция <element> ) в возвращаемом списке.

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

[x for x in range(10) if x % 2 == 0]
>>>Out: [0, 2, 4, 6, 8]

Приведенный выше код эквивалентен:

even_numbers = [] 
for x in range(10):
    if x % 2 == 0:
        even_numbers.append(x)

print(even_numbers)
>>>Out: [0, 2, 4, 6, 8]

Кроме того , усвоение условного списка вида [e for x in y if c] , где  e и c являются выражениями в терминах x ) эквивалентно list(filter(lambda x: c, map(lambda x: e, y)))

Несмотря на предоставление того же результата, обратите внимание на тот факт, что первый пример почти в 2 раза быстрее, чем второй. Для тех , кому любопытно, это хорошее объяснение причин почему.

Обратите внимание , что это совершенно отличается от ... if ... else ... условного выражения (иногда известное как трехкомпонентное выражение ) , которые вы можете использовать для <expression> часть списка понимания. Рассмотрим следующий пример:

[x if x % 2 == 0 else None for x in range(10)]

>>>Out: [0, None, 2, None, 4, None, 6, None, 8, None]

Здесь условное выражение - не фильтр, а оператор, определяющий значение, которое будет использоваться для элементов списка:

<value-if-condition-is-true> if <condition> else <value-if-condition-is-false>

 

Это становится более очевидным, если вы объедините его с другими операторами:

[2 * (x if x % 2 == 0 else -1) + 1 for x in range(10)]

>>>Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Приведенный выше код эквивалентен:

numbers = []
for x in range(10):
    if x % 2 == 0:
        temp = x
    else:
        temp = -1
    numbers.append(2 * temp + 1)
print(numbers)

>>>Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Можно комбинировать тройные выражения и if условия. Тернарный оператор работает с отфильтрованным результатом:

[x if x > 2 else '*' for x in range(10) if x % 2 == 0]

>>>Out: ['*', '*', 4, 6, 8] 

То же самое не могло быть достигнуто только одним троичным оператором:

[x if (x > 2 and x % 2 == 0) else '*' for x in range(10)]

# Out:['*', '*', '*', '*', 4, '*', 6, '*', 8, '*']
 

Смотрите также: фильтры , которые часто обеспечивают достаточную альтернативу условных списковые.

Перечень списков с помощью вложенных циклов

Списки могут использовать вложенный for . Вы можете закодировать любое количество вложенных циклов для внутри списка понимания, и каждый for цикла может иметь дополнительный связанный , if тест. При этом порядок следования for построений такой же порядок , как при написании серии вложенных for заявлений. Общая структура списочных представлений выглядит следующим образом:

[ expression for target1 in iterable1 [if condition1]
             for target2 in iterable2 [if condition2]...
             for targetN in iterableN [if conditionN] ]

 

Например, следующий код уплощение списка списков с использованием нескольких for операторов:

data = [[1, 2], [3, 4], [5, 6]]
output = []
for each_list in data:
    for element in each_list:
        output.append(element)
print(output)

>>>Out: [1, 2, 3, 4, 5, 6]

Можно эквивалентно записать в виде списка с кратной for конструкцией:

data = [[1, 2], [3, 4], [5, 6]]
output = [element for each_list in data for element in each_list]
print(output)
# Out: [1, 2, 3, 4, 5, 6]

 

Как в расширенной форме, так и в понимании списка, внешний цикл (первый для оператора) идет первым.

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

data = [[1,2],[3,4],[5,6]]
def f():
     output=[]
     for each_list in data:
         for element in each_list:
             output.append(element)
     return output
timeit f()

>>>Out: 529 ns ± 4.75 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

timeit [inner for outer in data for inner in outer]
>>>Out:360 ns ± 14.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
 

Накладные расходы на вызов функции выше примерно 170ns.

Встроенный , if ы вложены подобным образом , и может происходить в любом положении после первого for :

data = [[1], [2, 3], [4, 5]]
output = [element for each_list in data
                if len(each_list) == 2
                for element in each_list
                if element != 5]
print(output)

>>>Out: [2, 3, 4] 

Ради удобства чтения, однако, вы должны рассмотреть возможность использования традиционного для петель. Это особенно верно, когда вложение глубиной более 2-х уровней и / или логика понимания слишком сложна. многократное понимание списка вложенных циклов может быть подвержено ошибкам или дает неожиданный результат.

Фильтр рефакторинга и map для отображения списка

В filter или map функция часто должна быть заменена списком. Гвидо ван Россум описывает это хорошо в открытом письме в 2005 году :

Следующие строки коды считаются «не рабочими» и будут вызывать ошибки.

# четные числа < 10
filter(lambda x: x % 2 == 0, range(10))
>>>Out:<filter at 0x7fa5787f1c10>

# чтобы получить итоговый список ее надо преобразовать
list(filter(lambda x: x % 2 == 0, range(10)))
>>>Out:[0, 2, 4, 6, 8]

# умножение каждого числа на 2
map(lambda x: 2*x, range(10))
<map at 0x7fa5787f1590>

# чтобы вызвать ее надо преобразовать ее в список


# сумма всех элементов в списке 
functools.reduce(lambda x,y: x+y, range(10)) 

Принимая то , что мы узнали из предыдущей цитаты, мы можем сломать эти filter и map выражения в эквивалентных им списковые; также удаление лямбда - функции от каждого - сделать код более читаемым в этом процессе.

# Filter:
# P(x) = x % 2 == 0
# S = range(10)
[x for x in range(10) if x % 2 == 0]
>>>Out: [0, 2, 4, 6, 8]

# Map
# F(x) = 2*x
# S = range(10)
[2*x for x in range(10)]
>>>Out:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Читаемость становится еще более очевидной при работе с цепными функциями. Из-за читабельности, результаты одного map или функции фильтра должны быть переданы в качестве результата следующей; в простых случаях их можно заменить единый список. Кроме того, мы можем легко понять каков результат нашего процесса, где существует большая когнитивная нагрузка при рассуждениях о цепочечном процессе map и filter.

# Map & Filter
filtered = filter(lambda x: x % 2 == 0, range(10))
results = map(lambda x: 2*x, filtered)
print(list(results))

>>>Out: [0, 4, 8, 12, 16]


# List comprehension
results = [2*x for x in range(10) if x % 2 == 0]
print(results)

>>>Out: [0, 4, 8, 12, 16]

Рефакторинг - краткий справочник

map

map(F, S) == [F(x) for x in S] 

Фильтр

filter(P, S) == [x for x in S if P(x)] 

где F и P являются функциями , которые соответственно преобразуют входные значения и возвращают bool

Списковые включения с участием кортежей

for пункта в список понимания можно указать более одной переменные:

[x + y for x, y in [(1, 2), (3, 4), (5, 6)]]

>>>Out: [3, 7, 11]

[x + y for x, y in zip([1, 3, 5], [2, 4, 6])]

>>>Out: [3, 7, 11]

 

Это так же , как регулярные for петель:

for x, y in [(1,2), (3,4), (5,6)]:
    print(x+y)
    
>>>Out: 3
		7
		11

Однако обратите внимание, что если выражение, начинающее списковое включение, является кортежем, его необходимо заключить в скобки:

[x, y for x, y in [(1, 2), (3, 4), (5, 6)]]

>>>Out: SyntaxError: invalid syntax

[(x, y) for x, y in [(1, 2), (3, 4), (5, 6)]]

>>>Out: [(1, 2), (3, 4), (5, 6)]

Подсчет вхождений

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

# Подсчет числе в списке от 1 до 1000 (`range(1000)`), которые четные и содержат цифру 9 `9`:
print (sum(
    1 for x in range(1000) 
    if x % 2 == 0 and
    '9' in str(x)
))
>>>Out: 95

 

Основная концепция может быть обобщена как:

  1. Итерации над элементами в range(1000) .
  2. Сцепить все необходимое , при выполнении условия if .
  3. Используйте 1 в качестве выражения для возврата 1 для каждого элемента, который соответствует условиям.
  4. Суммируем все 1 , чтобы определить количество элементов , которые удовлетворяют условиям.

Примечание: Здесь мы не собираем все 1 в одном списке (обратите внимание на отсутствие квадратных скобок), но мы считаем их  непосредственно через функцию sum. Это называется выражение генератора, который похож на списковое включение.

Изменение типов в списке

Количественные данные часто считываются в виде строк, которые должны быть преобразованы в числовые типы перед обработкой. Типы всех элементов списка могут быть преобразованы либо в списковом включении или через функцию map().

# конвертируем список строк в целые числа.
items = ["1","2","3","4"]
[int(item) for item in items]

>>>Out: [1, 2, 3, 4]

# Конвертируем список строк в числа с плавающей точкой.
items = ["1","2","3","4"]
list(map(float, items))

>>>Out:[1.0, 2.0, 3.0, 4.0] 


Понимание вложенного списка

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

#Списковое включение с вложенным циклом
[x + y for x in [1, 2, 3] for y in [3, 4, 5]]
#Out: [4, 5, 6, 5, 6, 7, 6, 7, 8]

#Вложенное списковое включение
[[x + y for x in [1, 2, 3]] for y in [3, 4, 5]]
#Out: [[4, 5, 6], [5, 6, 7], [6, 7, 8]]

 

Вложенный пример эквивалентен

l = []
for y in [3, 4, 5]:
    temp = []
    for x in [1, 2, 3]:
        temp.append(x + y)
    l.append(temp)

Один пример, где вложенное списковое включение может быть использовано для транспонирования матрицы.

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

[[row[i] for row in matrix] for i in range(len(matrix))]
>>>Out: [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Как и вложенные for петель, есть не предел того , как глубоко постижения могут быть вложенными.

[[[i + j + k for k in 'cd'] for j in 'ab'] for i in '12']
>>>Out: [[['1ac', '1ad'], ['1bc', '1bd']], [['2ac', '2ad'], ['2bc', '2bd']]] 

Итерация двух или более списков одновременно в пределах понимания списка

Для перебора более двух списков одновременно в списковым включении, можно использовать zip() , как:

list_1 = [1, 2, 3 , 4]
list_2 = ['a', 'b', 'c', 'd']
list_3 = ['6', '7', '8', '9']

# 2 списка
[(i, j) for i, j in zip(list_1, list_2)]

>>>Out:[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]

# 3 списка
[(i, j, k) for i, j, k in zip(list_1, list_2, list_3)]
>>>Out:[(1, 'a', '6'), (2, 'b', '7'), (3, 'c', '8'), (4, 'd', '9')]

# итд