Абстракция списка Python

Содержание
Введение
Синтаксис
Условные операторы
Фильтрация в отдельной функции
Предварительное изменение
Несколько источников/условий
Вложенные абстракции
Похожие статьи

Введение

List Comprehension - Абстракция списков или списковое включение или генератор списков.

Документация

Из глоссария к официальной документации

list comprehension A compact way to process all or part of the elements in a sequence and return a list with the results. result = ['{:#04x}'.format(x) for x in range(256) if x % 2 == 0] generates a list of strings containing even hex numbers (0x..) in the range from 0 to 255. The if clause is optional. If omitted, all elements in range(256) are processed.

Абстракция списков или списковое включение (англ. list comprehension) в синтаксисе некоторых языков программирования — это способ компактного описания операций обработки списков.

Список можно создать и без применения абстракции списка. Например, с помощью цикла for … in

>>> squares = [] >>> for i in range(10): squares.append(i * i) >>> squares

Изучив синтаксис абстракции списка можно переписать это решение в одну строку вместо двух.

>>> squares = [i * i for i in range(10)] >>> squares

Результат в обоих случаях одинаковый

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

Базовый синтаксис

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

new_list = [expression for member in iterable]

[ expr(item) for item in iterable ]

Если провести аналогии с математикой

[ f(x) for x in ordered_set ]

f(x) - это какая-то функция от x. Начиная с самого простого f(x) = x, f(x) = 2x и т.д. А перебор должен идти по какому-то упорядоченному множеству, в Python это называется итерируемым объектом (iterable)

Каждая абстракция списка в Python состоит из цикла цикла for … in и ещё минимум трёх элементов:

expression, member, iterable

expression - выражение.

Это может быть как просто сам member, так и вызов какой-то функции. В общем случае expression - это любое другое валидное выражение, которое возвращает значение.

Чаще всего это какая-то функция где аргумент это member

f(member)

member это объект или значение в списке или другом итерируемом объекте .

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

Рассмотрим пример

squares = [i * i for i in range(10)]

В данном примере:

member - это i.

expression (выражение) - это i * i. То есть квадрат значения member.

iterable это range(10).

В некоторых примерах я пишу не просто expression, а

expr(item)

или

f(x)

но в принципе expression может вообще никак не быть связанным с member.

Рассмотрим пример ещё проще прошлого

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

[7, 7, 7]

Каждый раз когда цикл проходит по элементу списка [1, 2, 3] в новый список добавляется 7.

7 добавится вне зависимости от того, что находится в списке. Это тот случай, когда в математике бы написали f(x) = 7, а в общем случае f(x) = const

Тем не менее в большинстве случаев зависимость expression от member присутствует.

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

sites = ["eth1.ru", "heihei.ru", "devhops.ru", "topbicycle.ru"] print([len(site) for site in sites]) print([len(x) for x in sites])

[6, 9, 10, 13]
[6, 9, 10, 13]

site понятнее при чтении когда, поэтому я предпочитаю этот вариант. Но c x строка получается короче, что может пригодится чтобы избежать переноса слишком длинной строки .

Потому что выражение (expression) довольно гибко для вариаций, абстракция списка работает хорошо там, где можно было бы использовать map()

Например, на основе списка базовых цен txns, с помощью функции get_price_with_tax() вычислим стоимости с учетом налога

txns = [1.09, 23.56, 57.84, 4.56, 6.78] TAX_RATE = .08 def get_price_with_tax(txn): return txn * (1 + TAX_RATE) final_prices = [get_price_with_tax(i) for i in txns] print(final_prices)

[1.1772000000000002, 25.4448, 62.467200000000005, 4.9248, 7.322400000000001]

Решение этой же задачи с помощью функции map() можете изучить здесь .

Единственная разница между нашим решением и использованием map() состоит в том, что абстракция списка в Python возвращает список а map() возвращает map object.

Выгода от использования абстракции списка

Абстракции списка (List comprehensions) часто описывают как более «питоновский» способ создания списков чем циклы или map() .

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

В этой статье вы увидите несколько примеров в которых абстракция списка это лучший выбор.

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

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

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

Это основная причина, по которой абстракция списков считается «питоновским» подходом, поскольку Python включает в себя простые, мощные инструменты, которые вы можете использовать в самых разных ситуациях.

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

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

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

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

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

Как добавить функциональности абстракциям списка

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

Вы также захотите понять изменения, которые вносятся в понимание списка в Python 3.8.

Изображение баннера

Условные операторы

Использование Conditional Logic

Ранее вы уже видели формулу создания list comprehensions:

new_list = [expression for member in iterable]

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

Самый распростанённый способ добавить условие (conditional logic) к абстракции списка - это добавить его в конец выражения

new_list = [expression for member in iterable (if conditional)]

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

Условные выражения полезны тем, что они позволяют абстракциям списков отфильтровывать нежелательные значения без вызова функции filter():

Пример с условием:

sites = ["eth1.ru", "heihei.ru", "devhops.ru", "topbicycle.ru"] it = ["eth1.ru", "devhops.ru"] print([len(site) for site in sites if site in it])

[6, 10]

Ещё один пример

>>> sentence = 'the rocket came back from mars' >>> vowels = [i for i in sentence if i in 'aeiou'] >>> vowels

['e', 'o', 'e', 'a', 'e', 'a', 'o', 'a']

В этом примере условное выражение отфильтровывает все символы, которые не являются гласными.

Ещё один пример - отсеивание все чисел, которые не являются простыми.

from math import sqrt from pprint import pprint as pp def is_prime(x): if x < 2: return False for i in range(2, int(sqrt(x)) + 1): if x % i == 0: return False return True print([x for x in range(101) if is_prime(x)])

python filtering_compreh.py

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

Фильтр в отдельной функции

С помощью условий можно проверить любое валидное выражение. Если нужна более сложная фильтрация её можно вынести в отдельную функцию:

sentence = 'The rocket, who was named Ted, came back \ from Mars because he missed his friends.' def is_consonant(letter): vowels = 'aeiou' return letter.isalpha() and letter.lower() not in vowels consonants = [i for i in sentence if is_consonant(i)] print(consonants)

['T', 'h', 'r', 'c', 'k', 't', 'w', 'h', 'w', 's', 'n', 'm', 'd', \ 'T', 'd', 'c', 'm', 'b', 'c', 'k', 'f', 'r', 'm', 'M', 'r', 's', 'b', \ 'c', 's', 'h', 'm', 's', 's', 'd', 'h', 's', 'f', 'r', 'n', 'd', 's']

В этом примере создан сложный фильтр is_consonant() который передаётся в качестве условного выражения в абстракцию списка.

Обратите внимание, что member значение i передаётся как аргумент в функцию.

Про фильтрацию keyword arguments читайте статью *args, **kwargs

Предварительное изменение

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

new_list = [expression (if conditional) for member in iterable]

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

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

>>> original_prices = [1.25, -9.45, 10.22, 3.78, -5.92, 1.16] >>> prices = [i if i > 0 else 0 for i in original_prices] >>> prices

[1.25, 0, 10.22, 3.78, 0, 1.16]

В этом примере выражение i содержит условное выражение if i > 0 else 0.

Это говорит Python вывести значение i если число положительное, но если число отрицательное вывести 0.

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

>>> def get_price(price): ... return price if price > 0 else 0 >>> prices = [get_price(i) for i in original_prices] >>> prices

[1.25, 0, 10.22, 3.78, 0, 1.16]

Теперь ваше условное выражение содержится внутри функции get_price(), и вы можете использовать его как часть выражения для абстракции списка.

Изображение баннера

Несколько источников/условий

Если источником данных являются списки, вложенные в другой список - можно применить for несколько раз

a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] b = [x for i in a for x in i] print(b)

python list_comp.py

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

# Multi-input Comprehensions c = [(x, y) for x in range(5) for y in range(5)] print(c) # Without comprehension points = [] for x in range(5): for y in range(5): points.append((x, y)) print(points)

[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)] [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]

# Multiple if-clauses # Later clauses are nested inside earlier clauses values = [x / (x -y) for x in range(10) if x > 5 for y in range(10) if x - y != 0] print(values) # Without comprehension values = [] for x in range(10): if x > 5: for y in range(10): if x - y != 0: values.append(x / (x - y)) print(values)

[1.0, 1.2, 1.5, 2.0, 3.0, 6.0, -6.0, -3.0, -2.0, 1.0, 1.1666666666666667, 1.4, 1.75, 2.3333333333333335, 3.5, 7.0, -7.0, -3.5, 1.0, 1.1428571428571428, 1.3333333333333333, 1.6, 2.0, 2.6666666666666665, 4.0, 8.0, -8.0, 1.0, 1.125, 1.2857142857142858, 1.5, 1.8, 2.25, 3.0, 4.5, 9.0] [1.0, 1.2, 1.5, 2.0, 3.0, 6.0, -6.0, -3.0, -2.0, 1.0, 1.1666666666666667, 1.4, 1.75, 2.3333333333333335, 3.5, 7.0, -7.0, -3.5, 1.0, 1.1428571428571428, 1.3333333333333333, 1.6, 2.0, 2.6666666666666665, 4.0, 8.0, -8.0, 1.0, 1.125, 1.2857142857142858, 1.5, 1.8, 2.25, 3.0, 4.5, 9.0]

c = [(x, y) for x in range(5) for y in range(x)] print(c)

[(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3)]

Вложенные абстракции

# Nested Comprehensions vals = [[y * 3 for y in range(x)] for x in range(5)] print(vals) # Without Comprehension outer = [] for x in range(5): inner = [] for y in range(x): inner.append(y * 3) outer.append(inner) print(outer)

[[], [0], [0, 3], [0, 3, 6], [0, 3, 6, 9]] [[], [0], [0, 3], [0, 3, 6], [0, 3, 6, 9]]

Пример из статьи про range()

Похожие статьи
Встроенные коллекции
Списки []
list comprehension: Генератор списков
Python
if, elif, else
Циклы
Генератор словарей
Генератор множеств
Изображение баннера

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@urn.su если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящую по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: