В Python е лесно да се използва нотация за разбиране на списъци, когато се генерира нов списък.(List comprehensions
)
- 5. Data Structures — List Comprehensions — Python 3.10.0 Documentation
- 6. Expressions — Displays for lists, sets and dictionaries — Python 3.10.0 Documentation
В тази статия първо ще обсъдим следното
- Основен тип запис за разбиране на списък
- Запис за разбиране на списък с условно разклонение чрез if
- Комбинация с тройни оператори (обработка, подобна на if else)
zip()
,enumerate()
Комбинация с тези- запис за включване на вложен списък
След това ще обясним набора от нотации за разбиране на списъци с примерен код.
- запис за включване на набор(
Set comprehensions
) - запис за включване на речник(
Dict comprehensions
) - тип генератор(
Generator expressions
)
- Основен тип запис за разбиране на списък
- Запис за разбиране на списък с условно разклонение чрез if
- Комбинация с тройни оператори (обработка, подобна на if else)
- Комбинация с zip() и enumerate()
- запис за включване на вложен списък
- запис за включване на набор(Set comprehensions)
- запис за включване на речник(Dict comprehensions)
- тип генератор(Generator expressions)
Основен тип запис за разбиране на списък
Нотацията за разбиране на списък се записва по следния начин.
[Expression for Any Variable Name in Iterable Object]
Той взема всеки елемент на итерабилен обект, като списък, кортеж или диапазон, от произволно име на променлива и го оценява с израз. Връща се нов списък с резултата от оценката като елемент.
Даден е пример и еквивалентна декларация for.
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]
Само елементите на обекта iterable, чийто условен израз е true, се оценяват чрез израза и се връща нов списък, чиито елементи са резултатът.
Можете да използвате всяко име на променлива в условния израз.
Даден е пример и еквивалентна декларация for.
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]
Същият процес може да се извърши и с филтър(), но записът за разбиране на списък е предпочитан поради своята простота и яснота.
Комбинация с тройни оператори (обработка, подобна на if else)
В примера по-горе се обработват само елементите, които отговарят на критериите, а тези, които не отговарят на критериите, се изключват от новия списък.
Ако искате да превключите процеса в зависимост от условието или ако искате да обработите елементите, които не отговарят на условието, по различен начин, както в 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]
Даден е пример и еквивалентна декларация for.
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']
Възможно е също така да се записват изрази, като се използват произволни имена на променливи за стойностите true и false.
Ако условието е изпълнено, се извършва някаква обработка, в противен случай стойността на оригиналния итерабилен обект се оставя непроменена.
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]
запис за включване на вложен списък
Подобно на вложените цикли for, записът за разбиране на списък също може да бъде вложен.
[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 ... ]
За удобство са добавени прекъсвания на редовете и тирета, но те не са задължителни за граматиката; те могат да продължат на един ред.
Даден е пример и еквивалентна декларация for.
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'>
Пример за генераторен израз. Ако отпечатате() генератора в този му вид, той няма да отпечата съдържанието си, но ако го стартирате с израз 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
Генераторните изрази също така позволяват условно разклоняване и влагане с помощта на if, както и на запис за разбиране на списък.
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'>