Достижения в программировании всегда заключались в поиске лучших абстракций и идиом для выражения определенной части логики и решения проблемы. Фильтрация и сопоставление списка элементов данных — это старая проблема программирования, поскольку программирование существует, и количество способов решения этой проблемы в разных языках программирования и парадигмах огромно. Давайте посмотрим пример. Допустим, у нас есть список словарей, представляющих некоторых абитуриентов, и мы хотим сузить список до людей старше 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.