У 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
- Камбінацыя з патройнымі аператарамі (калі іншая апрацоўка)
zip()
,enumerate()
Спалучэнне з гэтымі- абазначэнне ўкладзенага спісу
Далей мы растлумачым набор натацый для разумення спісу з узорам кода.
- задаць абазначэнне ўключэння(
Set comprehensions
) - слоўнік уключэння абазначэння(
Dict comprehensions
) - тып генератара(
Generator expressions
)
- Асноўны тып абазначэння разумення спісу
- Абазначэнне разумення спісу з умоўным разгалінаваннем па 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'>