Tule je še nekaj stvari, ki sicer niso nič posebnega, le na predavanju ni bilo časa zanje.
Recimo, da bi kdo iz nekega neznanega razloga hotel napisati takšen program.
= ["Anže", "Benjamin", "Cene"]
tipi = ["Ana", "Berta", "Cilka", "Dani"]
tipinje
= []
pari for tip in tipi:
for tipinja in tipinje:
pari.append((tip, tipinja))print(pari)
[('Anže', 'Ana'), ('Anže', 'Berta'), ('Anže', 'Cilka'), ('Anže', 'Dani'), ('Benjamin', 'Ana'), ('Benjamin', 'Berta'), ('Benjamin', 'Cilka'), ('Benjamin', 'Dani'), ('Cene', 'Ana'), ('Cene', 'Berta'), ('Cene', 'Cilka'), ('Cene', 'Dani')]
Če hočeš to napisati z izpeljanim seznamom, tega ne narediš tako:
print([[(x, y) for y in tipinje] for x in tipi])
[[('Anže', 'Ana'), ('Anže', 'Berta'), ('Anže', 'Cilka'), ('Anže', 'Dani')], [('Benjamin', 'Ana'), ('Benjamin', 'Berta'), ('Benjamin', 'Cilka'), ('Benjamin', 'Dani')], [('Cene', 'Ana'), ('Cene', 'Berta'), ('Cene', 'Cilka'), ('Cene', 'Dani')]]
To namreč vrne seznam, ki vsebuje sezname.
Izpeljani seznami lahko vsebujejo tudi več zank. Pišemo jih z leve proti desni, najprej zunanjo.
for tip in tipi for tipinja in tipinje] [(tip, tipinja)
[('Anže', 'Ana'),
('Anže', 'Berta'),
('Anže', 'Cilka'),
('Anže', 'Dani'),
('Benjamin', 'Ana'),
('Benjamin', 'Berta'),
('Benjamin', 'Cilka'),
('Benjamin', 'Dani'),
('Cene', 'Ana'),
('Cene', 'Berta'),
('Cene', 'Cilka'),
('Cene', 'Dani')]
Seveda je dovoljen tudi if
.
for tip in tipi for tipinja in tipinje if tip[0] < tipinja[0]] [(tip, tipinja)
[('Anže', 'Berta'),
('Anže', 'Cilka'),
('Anže', 'Dani'),
('Benjamin', 'Cilka'),
('Benjamin', 'Dani'),
('Cene', 'Dani')]
Zgodi pa se tudi - ali pa se zdaj dogaja prvič - da bi kdo hotel napisati takšen program.
= "5 9 4 12"
stevila
= []
s for stevilo in stevila.split():
= int(stevilo)
x * (x + 1))
s.append(x
print(s)
[30, 90, 20, 156]
Če bi se mu zahotelo to napisati z izpeljanim seznamom, bi bil navidez prisiljen storiti tako.
int(stevilo) * (int(stevilo) + 1) for stevilo in stevila.split()] [
[30, 90, 20, 156]
Pri tem bi ga upravičeno iritiralo, da mora dvakrat poklicati
int
.
Morda bi kdo pripomnil, da lahko uporabimo Walrusa.
= int(stevilo)) * (x + 1) for stevilo in stevila.split()] [(x :
[30, 90, 20, 156]
Kaj pa vem. Mogoče je prav to eden od primerov, kjer ga nočem videti. Zato ga tule niti ne bom razlagal. Prava rešitev je takšna:
* (x + 1) for x in (int(stevilo) for stevilo in stevila.split())] [x
[30, 90, 20, 156]
Pravzaprav ne, prava rešitev tule je uporabiti map
.
* (x + 1) for x in map(int, stevila.split())] [x
[30, 90, 20, 156]
Vendar se o map
-u nismo kaj prida pogovarjali, namen
tega besedila pa je povedati, da lahko v primeru, da je potrebno
pripraviti neko vrednost tako, da pokličemo funkcijo (tule le
int
, lahko pa gre za kaj bolj zapletenega), to storimo
tako, da generator spustimo čez drug generator).
Zanka prek slovarja, v katerega znotraj zanke dodajamo elemente ali pa jih brišemo, je prepovedana. Če poskušamo kaj takšnega, bo Python javil napako.
Zanka prek seznama, ki spreminja dolžino znotraj zanke, je običajno slaba ideja. Oziroma, je skoraj gotovo slaba ideja, če dodajamo ali brišemo elemente pred trenutno pozicijo. V nekaterih situacijah pa zna biti dobra ideja, če seznam dopolnjujemo, medtem ko gremo čezenj.
= [1]
s for x in s:
*= 2
x if x > 100:
break
s.append(x)
s
[1, 2, 4, 8, 16, 32, 64]
Ni, da se ne bi dalo živeti brez tega, včasih pa pride prav.
Če imamo seznam s
, bo *s
(kot unarni
operator, ne kot množenje) v vseh kontekstih, v katerih to smemo
napisati, pomenilo isto kot s[0], s[1], s[2], ...
in tako
do konca. To velja tudi za terke. In tudi za slovarje in množice in
karkoli, čez kar lahko gremo z zanko for
(ali bolj učeno,
za poljubno zaporedje s
), le da tam seveda ne moremo pisati
o s[0]
in tako naprej.
def f(x, y, z, u):
print(x, y, z, u)
= [1, 2]
a = (3, 4)
b = {1, 2, 3} d
0, *a, 4) f(
0 1 2 4
*a, *b) f(
1 2 3 4
14, *d) f(
14 1 2 3
*range(3), 5) f(
0 1 2 5
Pa tudi:
1, 2, 3, *a, 5, 6, *range(10, 12), *d] [
[1, 2, 3, 1, 2, 5, 6, 10, 11, 1, 2, 3]
*a, *b} {
{1, 2, 3, 4}
Če imamo več generatorjev (ali seznemov, terk ipd) in bi radi šli z
zanko prek njih, enega za drugim, uporabimo chain
iz modula
itertools
.
= [1, 2, 3]
s = [4, 5, 6] t
from itertools import chain
for x in chain(s, t, range(7, 10)):
print(x)
1
2
3
4
5
6
7
8
9
for x in chain((x ** 2 for x in range(3)), (x ** 3 for x in range(3))):
print(x)
0
1
4
0
1
8
Imejmo seznam seznam seznamov. Ali pa, recimo, množico terk. Skratka, imejmo nekaj, česar elementi vsebujejo elemente. Nekaj gnezdenega.
= [(1, 2, 3), (4, 5), (6, 7, 8, 9)] s
for x in chain(*s):
print(x)
1
2
3
4
5
6
7
8
9
list(chain(*s))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Razumemo? chain
-u podamo *s, torej kot argument dobi
vse, kar vsebuje s
.
*s) chain(
je v gornjem primeru isto kot
chain((1, 2, 3), (4, 5), (6, 7, 8, 9))
chain
bo izgeneriral vse elemente teh terk.
V jezikih, ki imajo flatten
, se temu reče
flatten
. :)