V Pythonu je pri ustvarjanju novega seznama preprosto uporabiti zapis za razumevanje seznamov.(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
V tem članku bomo najprej obravnavali naslednje
- Osnovna vrsta zapisa za razumevanje seznama
- Zapis za razumevanje seznama s pogojnim razvejanjem z if
- Kombinacija s ternarnimi operatorji (obdelava, podobna obdelavi if else)
zip()
,enumerate()
Kombinacija s temi- zapis vključevanja ugnezdenih seznamov
Nato bomo z vzorčno kodo razložili nabor zapisov za razumevanje seznamov.
- zapis za vključitev množice(
Set comprehensions
) - zapis za vključitev v slovar(
Dict comprehensions
) - tip generatorja(
Generator expressions
)
- Osnovna vrsta zapisa za razumevanje seznama
- Zapis za razumevanje seznama s pogojnim razvejanjem z if
- Kombinacija s ternarnimi operatorji (obdelava, podobna obdelavi if else)
- Kombinacija s funkcijama zip() in enumerate()
- zapis vključevanja ugnezdenih seznamov
- zapis za vključitev množice(Set comprehensions)
- zapis za vključitev v slovar(Dict comprehensions)
- tip generatorja(Generator expressions)
Osnovna vrsta zapisa za razumevanje seznama
Zapis za razumevanje seznama je zapisan na naslednji način.
[Expression for Any Variable Name in Iterable Object]
Vsak element iterabilnega objekta, kot je seznam, tuple ali območje, vzame s poljubnim imenom spremenljivke in ga ovrednoti z izrazom. Vrne se nov seznam z rezultatom vrednotenja kot elementom.
Naveden je primer in enakovredna izjava 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]
Enak postopek je mogoče izvesti s funkcijo map(), vendar je zaradi preprostosti in jasnosti bolj primeren zapis z razumevanjem seznama.
Zapis za razumevanje seznama s pogojnim razvejanjem z if
Možno je tudi pogojno razvejanje s funkcijo if. If v postfiksu zapišite na naslednji način.
[Expression for Any Variable Name in Iterable Object if Conditional Expression]
Z izrazom se ovrednotijo samo elementi objekta iterable, katerih pogojni izraz je resničen, in vrne se nov seznam, katerega elementi so rezultat.
V pogojnem izrazu lahko uporabite poljubno ime spremenljivke.
Naveden je primer in enakovredna izjava 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]
Enak postopek je mogoče izvesti s funkcijo filter(), vendar je zapis za razumevanje seznama zaradi preprostosti in jasnosti primernejši.
Kombinacija s ternarnimi operatorji (obdelava, podobna obdelavi if else)
V zgornjem primeru se obdelajo samo elementi, ki izpolnjujejo merila, elementi, ki ne izpolnjujejo meril, pa so izključeni iz novega seznama.
Če želite preklopiti postopek glede na pogoj ali če želite elemente, ki ne izpolnjujejo pogoja, obdelati drugače, kot v primeru if else, uporabite trojni operator.
V Pythonu lahko ternarni operator zapišemo na naslednji način
Value When True if Conditional Expression else Value When False
To se uporablja v izraznem delu zapisa za razumevanje seznama, kot je prikazano spodaj.
[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]
Naveden je primer in enakovredna izjava 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']
Prav tako je mogoče zapisati izraze z uporabo poljubnih imen spremenljivk za vrednosti true in false.
Če je pogoj izpolnjen, se opravi določena obdelava, sicer pa ostane vrednost prvotnega objekta iterable nespremenjena.
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]
Kombinacija s funkcijama zip() in enumerate()
Uporabni funkciji, ki se pogosto uporabljata v stavku for, sta zip(), ki združi več iterablov, in enumerate(), ki vrne vrednost in njen indeks.
Seveda je mogoče uporabiti zip() in enumerate() z zapisom za razumevanje seznama. To ni posebna sintaksa in ni težko, če upoštevamo skladnost z izjavo for.
Primer funkcije 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')]
Primer funkcije 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')]
Ideja je enaka kot pri uporabi 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')]
Vsak element se lahko uporabi tudi za izračun novega elementa.
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]
zapis vključevanja ugnezdenih seznamov
Podobno kot gnezdenje zank for se lahko gnezdi tudi zapis za razumevanje seznama.
[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 ... ]
Zaradi priročnosti so dodani prelomi vrstic in alineje, ki pa niso potrebni za slovnico; lahko se nadaljujejo v eni sami vrstici.
Naveden je primer in enakovredna izjava 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]
Uporabiti je mogoče tudi več spremenljivk.
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)]
Izvedete lahko tudi pogojno razvejitev.
cells = [(row, col) for row in range(3)
for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]
Prav tako je mogoče pogojno vejiti za vsak objekt iterable.
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)]
zapis za vključitev množice(Set comprehensions)
Če kvadratne oklepaje [] v zapisu za razumevanje seznama spremenite v oglate oklepaje {}, ustvarite množico (objekt tipa množica).
{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}
print(s)
# {0, 1, 4, 9, 16}
zapis za vključitev v slovar(Dict comprehensions)
Slovarji (objekti tipa dict) se lahko ustvarijo tudi z zapisom razumevanja.
{} ter določite ključ in vrednost v izraznem delu kot ključ: vrednost.
{Key: Value for Any Variable Name in Iterable Object}
Za ključ in vrednost lahko določite poljuben izraz.
l = ['Alice', 'Bob', 'Charlie']
d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}
Če želite ustvariti nov slovar iz seznama ključev in vrednosti, uporabite funkcijo 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}
tip generatorja(Generator expressions)
Če so oglati oklepaji [] v zapisu za razumevanje seznamov uporabljeni kot oglati oklepaji (), se namesto tuple vrne generator. To se imenuje generatorski izrazi.
Primer zapisa za razumevanje seznama.
l = [i**2 for i in range(5)]
print(l)
# [0, 1, 4, 9, 16]
print(type(l))
# <class 'list'>
Primer izraza generatorja. Če natisnete() generator, kot je, ne bo izpisal svoje vsebine, če pa ga zaženete z ukazom for, lahko dobite vsebino.
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
Generatorski izrazi omogočajo tudi pogojno razvejitev in gnezdenje z uporabo if in zapisa za razumevanje seznama.
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)
Če na primer seznam z velikim številom elementov ustvarimo z uporabo zapisa za razumevanje seznama in nato z zanko preidemo skozi stavek for, bo seznam z vsemi elementi ustvarjen na začetku, če uporabimo zapis za razumevanje seznama. Po drugi strani pa se pri uporabi izraza generator ob vsaki ponovitvi zanke elementi generirajo drug za drugim, s čimer se zmanjša količina porabljenega pomnilnika.
Če je izraz generatorja edini argument funkcije, lahko okrogle oklepaje () izpustite.
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
Kar zadeva hitrost obdelave, je zapis za razumevanje seznama pogosto hitrejši od zapisa generatorja, ko so obdelani vsi elementi.
Vendar se na primer pri presojanju s funkcijama all() ali any() rezultat določi ob prisotnosti false ali true, zato je lahko uporaba generatorskih izrazov hitrejša kot uporaba zapisa za razumevanje seznama.
Zapisa za razumevanje tuplov ni, če pa kot argument funkcije tuple() uporabite izraz generatorja, lahko ustvarite tupel v zapisu za razumevanje.
t = tuple(i**2 for i in range(5))
print(t)
# (0, 1, 4, 9, 16)
print(type(t))
# <class 'tuple'>