Основы списковых включений
Contents
Основы списковых включений¶
Для создания списков есть очень мощный инструмент list comprehensions, который на русском иногда называют списковыми включениями.
Создание списка копий и копирование списков¶
В самом простом своём варианте списковое включение позволяет копировать содержимое итерируемого объекта в новый список.
a_list = [1, 2, 3]
a_copy = [x for x in a_list]
print(a_copy)
[1, 2, 3]
Рассмотрим синтаксис:
[x for x in iterable]
[]
квадратные скобочки означают, что содержимое будет списком;x for x in iterable
означает, что в список попадут элементыx
, гдеx
пробегает поiterable
;
То, что попадёт в список необязательно должно зависеть от iterable
. Например, можно создать список одинаковых элементов.
zeros = [0 for i in range(5)]
print(zeros)
[0, 0, 0, 0, 0]
Результат очень похож на выражение [0] * 5
, но в данном случае выражение вычисляется заново на каждой итерации цикла. Т.е. можно действительно создать список разных списков.
many_empty_lists = [[] for i in range(5)]
many_empty_lists[0].append("Много разных списков")
print(many_empty_lists)
[['Много разных списков'], [], [], [], []]
one_empty_list = [[]] * 5
one_empty_list[0].append("Один список")
print(one_empty_list)
[['Один список'], ['Один список'], ['Один список'], ['Один список'], ['Один список']]
Ещё это можно проиллюстрировать на примере с генерацией случайных чисел.
from random import random
print([random()] * 3)
print([random() for _ in range(3)])
[0.7356432841560648, 0.7356432841560648, 0.7356432841560648]
[0.2206528569401075, 0.8297474878522821, 0.3362492619099857]
Аналог функции map¶
В python есть встроенная функция map, которая позволяет отображать функцию на элементы списка (или другого итерируемого объекта), т.е. применять какую-то функцию к всем элементам списка.
Note
Функция map
делает вычисления лениво. Концепция ленивых вычислений будет обсуждаться при обсуждении генераторов и итераторов.
Пока что надо знать, что, чтобы получить список, необходимо применить list
к результату функции map
.
import math
array = [1, 4, 9, 16]
array_1 = list(map(math.sqrt, array))
print(array_1)
def my_square(x):
return x * x
array_2 = list(map(my_square, array_1))
print(array_2)
[1.0, 2.0, 3.0, 4.0]
[1.0, 4.0, 9.0, 16.0]
Можно имитировать функцию map
с помощью списковых включений.
array_1 = [math.sqrt(x) for x in array]
array_2 = [my_square(x) for x in array_1]
print(array_1)
print(array_2)
[1.0, 2.0, 3.0, 4.0]
[1.0, 4.0, 9.0, 16.0]
При этом необязательно применять функцию. Допустимо любое выражение.
array_2 = [x * x for x in array_1]
print(array_2)
[1.0, 4.0, 9.0, 16.0]
Т.е. теперь синтаксис такой:
[expression(x) for x in iterable]
Аналог функции filter¶
В python есть встроенная функция фильтрации filter, которая принимает на вход функцию f
и итерируемый объект iterable
. Она оставляет только те элементы iterable
, на которых f
возвращает True
. Как и функция map
, функция filter
делает это лениво.
from random import randint
def is_even(x):
# if x % 2:
# return False
# else:
# return True
return False if x % 2 else True # Тернарный оператор
array = [randint(0, 100) for _ in range(20)]
only_even = list(filter(is_even, array))
only_even_squared = list(map(my_square, filter(is_even, array)))
print(array)
print(only_even)
print(only_even_squared)
[14, 6, 67, 67, 37, 17, 41, 7, 8, 2, 78, 75, 90, 35, 74, 19, 63, 29, 92, 50]
[14, 6, 8, 2, 78, 90, 74, 92, 50]
[196, 36, 64, 4, 6084, 8100, 5476, 8464, 2500]
Того же самого можно добиться с помощью списковых включений.
only_even = [x for x in array if x % 2 == 0]
only_even_squared = [x * x for x in array if x % 2 == 0]
print(only_even)
print(only_even_squared)
[14, 6, 8, 2, 78, 90, 74, 92, 50]
[196, 36, 64, 4, 6084, 8100, 5476, 8464, 2500]
Т.е. теперь синтаксис такой:
[expression(x) for x in iterable if condition(x)]
Аналогия с множествами из математики¶
Иногда можно провести аналогию, между списковыми включениями и тем, как задаются множества в математике.
В качестве примера, рассмотрим множество заданной в виде
В этом примере, новое множество задаётся в виде некоторого выражения над переменной, которая пробегает по другому множеству, что очень хорошо ложится на синтаксис списковых включений:
R = [x ** 2 for x in range(-9, 10) if x % 2]
print(R)
[81, 49, 25, 9, 1, 1, 9, 25, 49, 81]
Важная деталь, которую стоит подметить — в математике множество не может содержать дубликатов, а список в python может. Благо, есть встроенный тип данных set, который ведет себя куда более похоже на математическое множество и тоже поддерживает синтаксис включений:
R = {x ** 2 for x in range(-9, 10) if x % 2}
print(R)
{1, 9, 81, 49, 25}
“Словарные включения”¶
Словари тоже поддерживают похожий синтаксис. Например, словарь, отображающий строчные буквы английского алфавита, на прописные можно записать следующим образом.
from string import ascii_lowercase
d = {symbol: symbol.upper() for symbol in ascii_lowercase}
print(d)
{'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E', 'f': 'F', 'g': 'G', 'h': 'H', 'i': 'I', 'j': 'J', 'k': 'K', 'l': 'L', 'm': 'M', 'n': 'N', 'o': 'O', 'p': 'P', 'q': 'Q', 'r': 'R', 's': 'S', 't': 'T', 'u': 'U', 'v': 'V', 'w': 'W', 'x': 'X', 'y': 'Y', 'z': 'Z'}
Используя функцию zip
можно записать его иначе:
from string import ascii_lowercase, ascii_uppercase
d = {s:S for s, S in zip(ascii_lowercase, ascii_uppercase)}
print(d)
{'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D', 'e': 'E', 'f': 'F', 'g': 'G', 'h': 'H', 'i': 'I', 'j': 'J', 'k': 'K', 'l': 'L', 'm': 'M', 'n': 'N', 'o': 'O', 'p': 'P', 'q': 'Q', 'r': 'R', 's': 'S', 't': 'T', 'u': 'U', 'v': 'V', 'w': 'W', 'x': 'X', 'y': 'Y', 'z': 'Z'}
Все остальные элементы из списковых включений поддерживаются и словарными включениями. Сделаем отображение только согласных букв.
from string import ascii_lowercase, ascii_uppercase
vowels = ["a", "e", "i", "o", "u", "y"]
d = d = {s:S for s, S in zip(ascii_lowercase, ascii_uppercase) if s not in vowels}
print(d)
{'b': 'B', 'c': 'C', 'd': 'D', 'f': 'F', 'g': 'G', 'h': 'H', 'j': 'J', 'k': 'K', 'l': 'L', 'm': 'M', 'n': 'N', 'p': 'P', 'q': 'Q', 'r': 'R', 's': 'S', 't': 'T', 'v': 'V', 'w': 'W', 'x': 'X', 'z': 'Z'}
В качестве аналога фильтрации, можно рассмотреть здесь применение множеств из python
: оператор -
для двух множеств возвращает их разницу. Мы можем получить согласные буквы, вычтя из всех букв гласные.
print(set(ascii_lowercase) - set(vowels))
{'g', 'q', 'j', 'm', 'l', 'w', 'k', 'v', 'c', 'h', 'd', 't', 'p', 'f', 'b', 'z', 'r', 'x', 's', 'n'}
Больше возможностей¶
В одном списковом включении может быть несколько циклов.
array = [symbol * n for symbol in ["a", "b", "c"] for n in [1, 2, 3]]
print(array)
['a', 'aa', 'aaa', 'b', 'bb', 'bbb', 'c', 'cc', 'ccc']
Списковые включения могут быть вложенными друг в друга.
array = [[symbol * n for symbol in ["a", "b", "c"]] for n in [1, 2, 3]]
print(array)
[['a', 'b', 'c'], ['aa', 'bb', 'cc'], ['aaa', 'bbb', 'ccc']]
Матрица¶
matrix = [[j * 4 + i for i in range(4)] for j in range(3)]
print(matrix)
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]