Використання нотації розуміння списку Python

Бізнес

У Python легко використовувати нотацію осмислення списку під час створення нового списку.(List comprehensions)

У цій статті ми спочатку обговоримо наступне

  • Основний тип запису для розуміння списку
  • Позначення для розуміння списку з умовним розгалуженням за допомогою if
  • Комбінація з потрійними операторами (якщо інша обробка)
  • zip(),enumerate()Поєднання з цими
  • позначення вкладеного списку

Далі ми пояснимо набір позначень для розуміння списку зі зразком коду.

  • встановити нотацію включення(Set comprehensions)
  • позначення включення словника(Dict comprehensions)
  • тип генератора(Generator expressions)

Основний тип запису для розуміння списку

Позначення розуміння списку записується таким чином.

[Expression for Any Variable Name in Iterable Object]

Він приймає кожен елемент ітераційного об’єкта, такого як список, кортеж або діапазон, за довільною назвою змінної та оцінює його за допомогою виразу. Повертається новий список з результатом оцінки як елементом.

Наведено приклад разом із еквівалентом для оператора.

squares = [i**2 for i in range(5)]
print(squares)
# [0, 1, 4, 9, 16]
squares = []
for i in range(5):
    squares.append(i**2)

print(squares)
# [0, 1, 4, 9, 16]

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

Позначення для розуміння списку з умовним розгалуженням за допомогою if

Також можливе умовне розгалуження за допомогою if. Запишіть if у постфіксі таким чином.

[Expression for Any Variable Name in Iterable Object if Conditional Expression]

Виразом оцінюються лише ті елементи ітераційного об’єкта, умовний вираз яких є істинним, і повертається новий список, елементи якого є результатом.

Ви можете використовувати будь-яку назву змінної в умовному виразі.

Наведено приклад разом із еквівалентом для оператора.

odds = [i for i in range(10) if i % 2 == 1]
print(odds)
# [1, 3, 5, 7, 9]
odds = []
for i in range(10):
    if i % 2 == 1:
        odds.append(i)

print(odds)
# [1, 3, 5, 7, 9]

Той самий процес можна виконати за допомогою filter(), але позначення розуміння списку є кращим через його простоту та ясність.

Комбінація з потрійними операторами (якщо інша обробка)

У наведеному вище прикладі обробляються лише ті елементи, які відповідають критеріям, а ті, що не відповідають критеріям, виключаються з нового списку.

Якщо ви хочете переключити процес залежно від умови, або якщо ви хочете обробити елементи, які не задовольняють умову по-різному, як у if else, використовуйте потрійний оператор.

У Python тернарний оператор можна записати так

Value When True if Conditional Expression else Value When False

Це використовується в частині виразів нотації розуміння списку, як показано нижче.

[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]

Наведено приклад разом із еквівалентом для оператора.

odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
odd_even = []
for i in range(10):
    if i % 2 == 1:
        odd_even.append('odd')
    else:
        odd_even.append('even')

print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']

Також можна писати вирази, використовуючи довільні імена змінних для істинних і хибних значень.

Якщо умова виконується, виконується деяка обробка, інакше значення вихідного ітераційного об’єкта залишається незмінним.

odd10 = [i * 10 if i % 2 == 1 else i for i in range(10)]
print(odd10)
# [0, 10, 2, 30, 4, 50, 6, 70, 8, 90]

Комбінація з zip() і enumerate()

Корисні функції, які часто використовуються в операторі for, включають zip(), який поєднує кілька ітераторів, і enumerate(), який повертає значення разом із його індексом.

Звичайно, можна використовувати zip() і enumerate() із записом розуміння списку. Це не особливий синтаксис, і це не складно, якщо розглянути відповідність оператору for.

Приклад zip().

l_str1 = ['a', 'b', 'c']
l_str2 = ['x', 'y', 'z']

l_zip = [(s1, s2) for s1, s2 in zip(l_str1, l_str2)]
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
l_zip = []
for s1, s2 in zip(l_str1, l_str2):
    l_zip.append((s1, s2))

print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]

Приклад enumerate().

l_enu = [(i, s) for i, s in enumerate(l_str1)]
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
l_enu = []
for i, s in enumerate(l_str1):
    l_enu.append((i, s))

print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]

Ідея така ж, як і раніше, коли використовується if.

l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b']
print(l_zip_if)
# [('a', 'x'), ('c', 'z')]

Кожен елемент також можна використовувати для обчислення нового елемента.

l_int1 = [1, 2, 3]
l_int2 = [10, 20, 30]

l_sub = [i2 - i1 for i1, i2 in zip(l_int1, l_int2)]
print(l_sub)
# [9, 18, 27]

позначення вкладеного списку

Подібно вкладенню для циклів, позначення розуміння списку також може бути вкладеним.

[Expression for Variable Name 1 in Iterable Object 1
    for Variable Name 2 in Iterable Object 2
        for Variable Name 3 in Iterable Object 3 ... ]

Для зручності додано розриви рядків і відступи, але вони не є обов’язковими для граматики; їх можна продовжити в одному рядку.

Наведено приклад разом із еквівалентом для оператора.

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

flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
flat = []
for row in matrix:
    for x in row:
        flat.append(x)

print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Також можна використовувати декілька змінних.

cells = [(row, col) for row in range(3) for col in range(2)]
print(cells)
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]

Ви також можете зробити умовне розгалуження.

cells = [(row, col) for row in range(3)
         for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]

Також можна умовно розгалужуватися для кожного ітераційного об’єкта.

cells = [(row, col) for row in range(3) if row % 2 == 0
         for col in range(2) if col % 2 == 0]
print(cells)
# [(0, 0), (2, 0)]

встановити нотацію включення(Set comprehensions)

Зміна квадратних дужок [] у нотації для розуміння списку на фігурні дужки {} створює набір (об’єкт типу набору).

{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}

print(s)
# {0, 1, 4, 9, 16}

позначення включення словника(Dict comprehensions)

Словники (об’єкти типу dict) також можуть бути створені з позначенням розуміння.

{} і вкажіть ключ і значення у частині виразу як ключ: значення.

{Key: Value for Any Variable Name in Iterable Object}

Для ключа та значення можна вказати будь-який вираз.

l = ['Alice', 'Bob', 'Charlie']

d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}

Щоб створити новий словник зі списку ключів і значень, скористайтеся функцією zip().

keys = ['k1', 'k2', 'k3']
values = [1, 2, 3]

d = {k: v for k, v in zip(keys, values)}
print(d)
# {'k1': 1, 'k2': 2, 'k3': 3}

тип генератора(Generator expressions)

Якщо квадратні дужки [] у нотації осмислення списку використовуються як круглі дужки (), замість кортежу повертається генератор. Це називається генераторними виразами.

Приклад нотації для розуміння списку.

l = [i**2 for i in range(5)]

print(l)
# [0, 1, 4, 9, 16]

print(type(l))
# <class 'list'>

Приклад генераторного виразу. Якщо ви print() генератор як є, він не виведе його вміст, але якщо ви запустите його з оператором for, ви можете отримати вміст.

g = (i**2 for i in range(5))

print(g)
# <generator object <genexpr> at 0x10af944f8>

print(type(g))
# <class 'generator'>

for i in g:
    print(i)
# 0
# 1
# 4
# 9
# 16

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

g_cells = ((row, col) for row in range(0, 3)
           for col in range(0, 2) if col == row)

print(type(g_cells))
# <class 'generator'>

for i in g_cells:
    print(i)
# (0, 0)
# (1, 1)

Наприклад, якщо список з великою кількістю елементів генерується за допомогою нотації розуміння списку, а потім прокручується за допомогою оператора for, список, що містить усі елементи, буде створено на початку, якщо використовується нотація розуміння списку. З іншого боку, якщо ви використовуєте вираз генератора, кожен раз, коли цикл повторюється, елементи генеруються один за одним, таким чином зменшуючи обсяг використовуваної пам’яті.

Якщо вираз генератора є єдиним аргументом функції, круглі дужки () можна опустити.

print(sum([i**2 for i in range(5)]))
# 30

print(sum((i**2 for i in range(5))))
# 30

print(sum(i**2 for i in range(5)))
# 30

Що стосується швидкості обробки, то позначення розуміння списку часто швидше, ніж позначення генератора, коли обробляються всі елементи.

Однак, якщо судити за допомогою all() або any(), наприклад, результат визначається за наявності false або true, тому використання виразів генератора може бути швидшим, ніж використання нотацій для розуміння списку.

Немає позначень для розуміння кортежу, але якщо ви використовуєте вираз генератора як аргумент tuple(), ви можете створити кортеж у нотації розуміння.

t = tuple(i**2 for i in range(5))

print(t)
# (0, 1, 4, 9, 16)

print(type(t))
# <class 'tuple'>