Достижения в программировании всегда заключались в поиске лучших абстракций и идиом для выражения определенной части логики и решения проблемы. Фильтрация и сопоставление списка элементов данных — это старая проблема программирования, поскольку программирование существует, и количество способов решения этой проблемы в разных языках программирования и парадигмах огромно. Давайте посмотрим пример. Допустим, у нас есть список словарей, представляющих некоторых абитуриентов, и мы хотим сузить список до людей старше 21 года.

people = [
  {'name': 'Joe', 'age': 20},
  {'name': 'Ben', 'age': 24},
  {'name': 'Heather', 'age': 19},
  {'name': 'Harry', 'age': 27}
]

people_above_21 = []

for person in people:
  if person["age"] > 21:
    people_above_21.append(person)

Простого цикла for или даже цикла while более чем достаточно, чтобы выполнить эту работу. Но это низкоуровневые примитивы, доступные почти во всех языках. Более высокоуровневым и функциональным подходом было бы использование встроенной функции Python filter:

people_above_21 = list(filter(lambda person: person['age'] > 21, people))

# [{'name': 'Ben', 'age': 24}, {'name': 'Harry', 'age': 27}]

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

people_above_21 = [person for person in people if person['age'] > 21 ]

# [{'name': 'Ben', 'age': 24}, {'name': 'Harry', 'age': 27}]

Мне это кажется очень выразительным и гораздо яснее передает замысел автора. Это звучит почти как «выбрать каждого человека в списке people, чей возраст больше 21 года». Целью языков программирования всегда было общение с людьми, а не с компьютерами, и это хорошо справляется с этой задачей.

Это называется пониманием. В частности, это понимание списка, потому что мы получаем список в результате этой операции. В более общем смысле понимание списка можно записать так:

[<expression_using_variable> for <variable> in <iterable> if <condition>]

Обратите внимание, что первой частью понимания может быть не только переменная, это может быть любое выражение, использующее эту переменную. То же самое относится и к выражению условия. Например, мы бы использовали следующее понимание, если бы хотели получить только возраст людей старше 21 года:

names_of_people_above_21 = [person['name'] for person in people if person['age'] > 21]

Также обратите внимание на <iterable> вместо <list>. Это означает, что мы можем использовать любой объект Python, который можно перебирать, включая строки:

[x for x in "hello"] # creates a list: ['h', 'e', 'l', 'l', 'o']

Вышеизложенное также показывает, что часть условия понимания является необязательной. Если вы опустите его, операция станет похожей на встроенную функцию map. Другим примером такого использования может быть, если мы хотим получить имена всех людей в списке people. По сути, нам нужен список, который сопоставляет каждого человека в списке с его/ее именем:

names = [person['name'] for person in people] # ['Joe', 'Ben', 'Heather', 'Harry']

Понимание словаря

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

numbers = [1, 2, 3, 4]
square_dict = {n: n * n for n in numbers} # {1: 1, 2: 4, 3: 9, 4: 16}

Обратите внимание на использование фигурных скобок ({}) вместо скобок для понимания словаря. Мы также можем использовать условное здесь. Давайте получим словарь квадратов, который включает только четные числа:

square_dict = {n: n * n for n in numbers if n % 2 == 0} # {2: 4, 4: 16}

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