Recimo, da smo zbrali teže šestih študentov. Seznam tež zapišemo takole:
teze = [74, 82, 58, 66, 61, 84]
Seznam (angl. list)
je zaporedje česarkoli, recimo števil, lahko pa tudi česa drugega. Števila
ali kaj drugega naštejemo, vmes pišemo vejice in vse skupaj zapremo v
oglate oklepaje [
in ]
. Za primer sestavimo še
seznam imen študentov:
imena = ["Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc"]
in seznam, ki bo povedal, ali gre za študenta ali študentko
studentka = [False, False, True, False, True, False]
Seznami lahko vsebujejo tudi še hujšo eksotiko. Imamo lahko, recimo, seznam seznamov - vanj bomo, prikladno, stlačili (pod)sezname teža-ime-spol:
podatki = [ [74, "Anze", False], [82, "Benjamin", False], [58, "Cilka", True], [66, "Dani", False], [61, "Eva", True], [84, "Franc", False], ]
To sicer navadno delamo malo drugače (pogosto celo precej drugače), a dokler tega še ne znamo, bo dobro tudi tako. Mimogrede opazimo še dve stvari: seznam lahko, če želimo, raztegnemo v več vrst, preprosto tako, da ne zapremo oklepaja. Naredili bi lahko tudi tole
teze = [74, 82, 58, 66, 61, 84]
Seznami lahko vsebujejo tudi še hujšo hujšo eksotiko. V resnici lahko vsebujejo karkoli, celo, recimo, funkcije:
from math import * par_funkcij = [sin, cos, radians]
Čemu bi kdo hotel narediti seznam funkcij?! I, zato da jih bo klical, seveda. Pa kdaj kdo kliče funkcije s seznama?! Da, včasih pride prav.
Nikjer tudi ne piše, da morajo biti vsi elementi seznama istega tipa. To smo pravzaprav že izkoristili: podseznami s težo, imenom in spolom so vsebovali število, niz in logično vrednost. Navrgli bi lahko še dve funkciji in en seznam; pa ne bomo, to se ne dela. Če želimo sezname različnih tipov, raje uporabimo terke. Kaj so terke, bomo izvedeli vsak čas.)
Seznam je lahko tudi prazen
prazen = []
ali pa ima en sam element
samo_edini = [42]
Ali pa je poln praznih seznamov
polnoprazen = [[], [], [], [], [], []]
Terka (angl. tuple) je podobna seznamu. Kakšna je pomembna razlika, bomo še videli, manj pomembna pa je ta, da pri terki namesto oglatih oklepajev uporabljamo kar okrogle. (Skoraj) vse ostalo ostane enako:
teze = (74, 82, 58, 66, 61, 84) imena = ("Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc")
Pri terkah se nam - zaradi tega, kje in kako jih uporabljamo, pogosteje zgodi, da vsebuje elemente različnih tipov. Recimo takole:
student = (74, "Anze", False)
Tudi seznam teža-ime-spol bi raje naredil takole:
podatki = [ (74, "Anze", False), (82, "Benjamin", False), (58, "Cilka", True), (66, "Dani", False), (61, "Eva", True), (84, "Franc", False), ]
Tako kot seznami so tudi terke lahko prazne (()
) ali pa imajo
po en sam element. Tu imamo težavo pri terki z enim samim elementom: Če bi
napisali (42)
to ni seznam, temveč le 42 v oklepaju (odvečnem,
seveda). Da bi povedali, da gre za terko, moramo dodati še vejico:
samo_en = (42, )
Terko smemo, zanimivo, pisati tudi brez oklepajev.
>>> t = 1, 2, 3, 4 >>> t (1, 2, 3, 4)
Ker to ni preveč pregledno, pa to počnemo le v posebnih primerih, na katere bomo sproti opozorili.
Včasih imamo seznam ali terko z nekaj elementi, ki bi jih radi priredili več spremenljivkam. Recimo, da imamo
student = (74, "Anze", False)
Če želimo podatke prirediti spremenljivkam teza
,
ime
in je_zenska
, bi to lahko storili takole:
>>> teza, ime, je_zenska = student
Doslej smo imeli pri prirejanju na levi strani vedno eno samo spremenljivko, ki smo ji želeli prirediti vrednost na desni strani enačaja. Tu pa smo na levi našteli več imen spremenljivk (tri), na desni pa mora biti za to terka z enakim številom elementov (tremi). Terka? No, lahko je tudi seznam ali niz.
>>> a, b, c = [1, 2, 3] >>> a 1 >>> a, b, c = "xyz" >>> a 'x' >>> b 'y' >>> c 'z'
Več ko boste programirali v Pythonu, bolj boste čutili moč terk. Omenimo,
recimo, da je z njimi mogoče napisati funkcijo, ki ne vrača ene, temveč več
stvari. Vzemimo funkcijo splitext
, ki ji damo ime datoteke,
pa vrne njeno osnovno ime in njeno končnico.
>>> from os.path import * >>> film = "Babylon 5 - 3x04 - Passing through Gethsemane.avi" >>> zac, konc = splitext(film) >>> zac 'Babylon 5 - 3x04 - Passing through Gethsemane' >>> konc '.avi'
V resnici funkcija splitext
vrne terko,
>>> from os.path import * >>> splitext(film) ('Babylon 5 - 3x04 - Passing through Gethsemane', '.avi')
ki pa smo jo kar mimogrede razpakirali v zac
in
konc
.
Seveda bi lahko rezultat funkcije priredili tudi eni sami spremenljivki - v tem primeru bi bila ta spremenljivka terka.
>>> t = splitext(film) >>> t ('Babylon 5 - 3x04 - Passing through Gethsemane', '.avi')
In seveda bi lahko terko nato še razpakirali.
ime, koncnica = t
Kako v Pythonu zamenjamo vrednosti dveh spremenljivk?
a = "Alenka" b = "Tina"
Želeli bi, da bi a
postal "Tina" in b
"Alenka".
Naivnež bi napisal tale nesmisel:
a = b b = a
To seveda ne deluje. V prvi vrstici a
-ju priredimo
b
, se pravi, "Tina", v drugi vrstici pa b
-ju
a
, ki pa je medtem postal "Tina". Tako sta po tem
a
in b
enaka "Tina".
V drugih jezikih se navadno rešimo tako, da vrednost a
-ja
spravimo na varno, preden ga povozimo.
tmp = a a = b b = tmp
V Pythonu pa to ni potrebno. Spremenljivki preprosto zapakiramo v terko, ki jo takoj razpakiramo nazaj, a v obratnem vrstnem redu.
a, b = (b, a)
V resnici navadno ne pišemo tako, temveč izpustimo oklepaje.
a, b = b, a
Oklepaje okrog terke izpuščamo preprosto zato, ker je takšno prirejanje
tako pogosto, da ga programer vajen Pythona takoj prepozna. Pri tem tudi
ne razmišljamo o terkah, temveč prirejanje preberemo preprosto tako, da
a
-ju priredimo b
in b
-ju
a
.
Python ima dve vrsti zank: zanki while
, ki jo že poznamo, dela
družbo for
.
Zastavimo si preprosto nalogo (in ne čisto smiselno) nalogo: izpišimo teže in kvadrate tež vseh študentov v seznamu. Se pravi (po slovensko):
za vsako težo s seznama teze stori tole: izpiši težo in težo na kvadrat
V Pythonu pa prav tako, samo brez sklonov:
for teza in teze: print(teza, teza ** 2)
V zanki for
se skriva prirejanje. Zanka najprej
priredi spremenljivki teza
prvi element seznama
teze
. Nato se izvrši vsa koda bloka znotraj bloka
for
- tako, kot se je dogajalo v zanki while
.
V naslednji rundi priredi spremenljivki teza
drugi element in
spet izvede kodo znotraj bloka, ... ter tako naprej do konca seznama.
Zdaj pa izpišimo vse teže, ki so večje od 70.
for teza in teze: if teza > 70: print(teza)
Napišimo program, ki pove, kako težak je najlažji študent.
najlazji = 1000 for teza in teze: if teza < najlazji: najlazji = teza print(najlazji)
Je potrebno prevesti v slovenščino? Pa dajmo.
za začetek naj bo najlažja teža 1000 (gotovo bomo kasneje našli koga lažjega) za vsako težo iz seznama tež: če je teža manjša od najmanjše doslej: najlažja je ta teža izpiši najmanjšo težo
Deluje ta program samo na seznamih števil? Ali pa bi znal poiskati tudi
najmanjši niz? Kako pravzaprav primerjamo nize? Nize primerja, seveda, po
abecedi. In, da, program deluje tudi na nizih, vrne "najmanjši" niz - prvi
niz po abecedi. Le v začetku moramo napisati
najlazji = "ŽŽŽŽŽŽŽŽŽŽŽŽ"
namesto najlazji = 1000
.
(Tole se da sprogramirati tudi tako, da deluje za nize in za števila. A ne
bomo ubijali začetnikov s prezapletenimi programi.)
Mimogrede, Python ima že vdelano funkcijo min
, ki vrne
najmanjši element seznama.
Kako bi izračunali vsoto elementov seznama?
s = 0 for teza in teze: s += teza
Pa poprečno vrednost? Za to moramo poznati dolžino seznama. Pove nam jo
funkcija len
(okrajšava za length, če ji kot argument
podamo nek seznam).
s = 0 for teza in teze: s += teza s /= len(teze)
Samo... malo previdni moramo biti. Seznam bi lahko bil tudi prazen. Dogovorimo se, da bo poprečna vrednost v tem primeru 0. Pravilno delujoč program bi bil takšen.
s = 0 for teza in teze: s += teza if len(teze) > 0: s /= len(teze)
Zanka for ne deluje le na seznamih. Tako, kot gre prek seznamov, lahko
gre tudi prek terk, nizov in še prek mnogih drugih reči. V Pythonu celo za
branje datotek pogosto uporabimo for
, kot se bomo kmalu
naučili.
>>> ime = "Cilka" >>> for crka in ime: ... print(crka) 'C' 'i' 'l' 'k' 'a'
Kako bi ugotovili, ali vsebuje seznam kako sodo število?
s = [11, 13, 5, 12, 5, 16, 7] imamo_sodo = False for e in s: if e % 2 == 0: imamo_sodo = True if imamo_sodo: print("Seznam vsebuje sodo število") else: print("Seznam ne vsebuje sodega števila")
V začetku si rečemo, da nimamo nobenega sodega števila. Nato gremo prek vseh števil in čim naletimo na kakega sodega, zabeležimo, da smo, aleluja, našli sodo število.
Ne naredite klasične napake. Tole je narobe:
s = [11, 13, 5, 12, 5, 16, 7] imamo_sodo = False for e in s: if e % 2 == 0: imamo_sodo = True else: imamo_sodo = False if imamo_sodo: print("Seznam vsebuje sodo število") else: print("Seznam ne vsebuje sodega števila")
Ta program se bo ob vsakem lihem številu delal, da doslej ni bilo še
nobenega sodega - zaradi imamo_sodo = False
bo pozabil, da je
kdajkoli videl kako sodo število. V gornjem seznamu bo, recimo, pregledal
vsa števila, končal bo s 7 in si ob tem rekel imamo_sodo = False
.
Rezultat bo tako napačen.
Kako pa ugotovimo, ali ima seznam sama soda števila?
sama_soda = True for e in s: if e % 2 != 0: sama_soda = False if sama_soda: print("Seznam vsebuje sama soda število") else: print("Seznam ne vsebuje samo sodih števil")
Spet bomo naredili podobno napako kot prej, če bomo pisali:
s = [11, 13, 5, 12, 5, 16, 72] sama_soda = True for e in s: if e % 2 != 0: sama_soda = False else: sama_soda = True
Program že takoj, ko vidi 1, ugotovi, da niso vsa števila v seznamu soda,
in postavi sama_soda = False
. Vendar melje seznam naprej in ob
vsakem koraku znova nastavlja sama_soda
. Ko pride do 72, postavi
sama_soda
na True
. To pa je ravno zadnje število;
sama_soda
ostane True
... pa smo tam.
Ko smo prejšnji teden pisali zanko while
, smo postavili pogoj,
do kdaj naj se izvaja. Zanka for bo, če se vmes ne pripeti kaj posebnega,
šla vedno od začetka do konca seznama (terke, niza, datoteke...)
Včasih to ni potrebno. Pravzaprav smo pravkar videli takšen primer: rezultat gornjega programa je znan že, čim naletimo na prvo liho število. Torej bi bilo čisto vseeno, če računalnik v tistem trenutku neha pregledovanje. To mu v resnici lahko naročimo.
sama_soda = True for e in s: if e % 2 != 0: sama_soda = False break if sama_soda: print("Seznam vsebuje sama soda število") else: print("Seznam ne vsebuje samo sodih števil")
Ukaz break
pomeni, da želimo prekiniti zanko. Program skoči
"ven" iz zanke in nadaljuje z izvajanjem ukazov, ki sledijo zanki.
Mimogrede, na podoben način lahko prekinemo tudi while
.
Tule je bil break
skoraj bolj zaradi lepšega - da računalnik
ne izgublja časa brez potrebe. Poskusimo napisati program, ki takrat, ko seznam
ni vseboval samo sodih števil, izpiše prvo liho število.
sama_soda = True for e in s: if e % 2 != 0: sama_soda = False liho = e if sama_soda: print("Seznam vsebuje samo soda števila") else: print("Seznam ne vsebuje samo sodih števil: prvo liho število je", liho)
Brez break
tole ne deluje: namesto prvega izpiše zadnje liho
število. Rešimo se lahko z dodatnim pogojem:
sama_soda = True for e in s: if e % 2 != 0 and sama_soda: sama_soda = False liho = e if sama_soda: print("Seznam vsebuje samo soda števila") else: print("Seznam ne vsebuje samo sodih števil: prvo liho število je", liho)
S tem, ko smo dodali and sama_soda
smo poskrbeli, da se bosta
sama_soda = False
in liho = e
izvedla le prvič. Ko
bomo postavili sama_soda
na False
, bomo dosegli, da
pogoj ne bo nikoli nikoli več resničen.
Namesto dodatnega pogoja lahko napišemo break
.
sama_soda = True for e in s: if e % 2 != 0 and sama_soda: sama_soda = False liho = e break if sama_soda: print("Seznam vsebuje samo soda števila") else: print("Seznam ne vsebuje samo sodih števil: prvo liho število je", liho)
S tem se program lahko pravzaprav še bolj poenostavi. Ko zanko prekinemo,
e
ostane, kar je bil. Torej ne potrebujemo več spremenljivke
liho
.
sama_soda = True for e in s: if e % 2 != 0: sama_soda = False break if sama_soda: print("Seznam vsebuje samo soda števila") else: print("Seznam ne vsebuje samo sodih števil: prvo liho število je", e)
Medtem, ko je ukaz break
zelo običajna žival v vseh
programskih jezikih, ima Python še eno posebnost, povezano z zankami. V
večini programskih jezikov lahko else
uporabimo kot "odgovor"
na if
. V Pythonu pa lahko else
sledi tudi zanki
for
ali while
. V pogojnem stavku (if
)
se koda v else
izvede, če pogoj ni bil resničen. Po zanki se
else
izvede, če se zanka ni prekinila zaradi
break
. Torej, else
se izvede pri zankah, ki so
se iztekle "po naravni poti".
Oglejmo si še enkrat gornji program. Kar smo počeli v njem, je kar pogosto: v zanki nekaj iščemo (recimo kako liho število). Če to reč najdemo, nekaj storimo (ga izpišemo) in prekinemo zanko. Sicer (torej, če ga ne najdemo), storimo kaj drugega (izpišemo, da ga nismo našli).
Zgornji odstavek se približno, a ne čisto povsem prilega zadnjemu kosu programa. V resnici je napisan po spodnjem programu:
for e in s: if e % 2 != 0: print("Seznam ne vsebuje samo sodih števil: prvo liho število je", e) break else: print("Seznam vsebuje samo soda števila")
Se pravi: če najdemo liho število, napišemo, da seznam vsebuje liho število
in prekinemo zanko. Ta del je jasen. Ne spreglejte pa, kje je else
:
poravnan je s for
ne z if
! Ta else
se torej nanaša na for
. Kar napišemo v else
-u za
for
, se zgodi, če se zanka ni prekinila z break
om.
Preden naredimo še kakšen primer, si oglejmo še nekaj drugega.
for
Tisti, ki poznajo kak drug, C-ju podoben jezik, so najbrž pričakovali nekoliko drugačno zanko.
Poglejmo resnici v oči: v C, C++, C#, Javi sta zanka for in while le dva načina, na katera povemo eno in isto.
for(zacetek; pogoj; korak) { koda; }
je isto kot
zacetek; while(pogoj) { koda; korak; }
Če je ideja zanke for v C-ju podobnih jezikih ta, da se z njo šteje, pa imamo v Pythonu za to nekaj priročnejšega in splošnejšega.
V programih pogosto potrebujemo še dva tipa zank. Pogosto bomo želeli nekaj ponoviti vnaprej predpisano oziroma pred zanko izračunano število ponovitev, recimo petkrat ali desetkrat. Prav tako bomo pogosto želeli, da program "šteje" od 10 do 20, od 0 do 100 ali kaj takega.
Za oboje bo poskrbela kar zanka for, ob pomoči priročne funkcije
range
.
Funkcija range
je na prvi pogled povsem neimpresivna. Vrača
sezname zaporednih števil.
V Pythonu 3.0 je ta funkcija narejena boljše kot v različicah pred njim, vendar je nova oblika za začetnika manj očitna, zato bomo za trenutek skočili v stari Python 2.7. Kar sledi, v novem Pythonu namreč ne bi dalo enakih izpisov.
>>> range(5, 10) [5, 6, 7, 8, 9] >>> range(10, 20) [10, 11, 12, 13, 14, 15, 16, 17, 18, 19] >>> range(7) [0, 1, 2, 3, 4, 5, 6] >>> range(16) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
Funkcija range
je vrnila seznam celih števil, ki se začne pri
prvi vrednosti in konča eno pred zadnjo. Koliko elementov ima seznam,
ki ga vrne range(7)
? Sedem. Zato
ker vsebuje prvi element in ne zadnjega, in ker se začne z 0.
Funkcija range
je torej narejena praktično, ker se težko zmotimo
glede tega, koliko elementov bo vrnila.
Lepo se tudi sešteva. range(10, 15) + range(15, 20)
so ravno
vsa števila od 10 do 20. Če bi bil range(10, 15)
enak
[10, 11, 12, 13, 14, 15]
, bi se v
range(10, 15) + range(15, 20)
število 15 ponovilo dvakrat.
Funkcija ima lahko še en, tretji argument. Ta predstavlja korak.
>>> range(5, 15, 2) [5, 7, 9, 11, 13]
Korak je lahko tudi negativen.
>>> range(10, 0, -1) [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] >>> range(10, 0, -3) [10, 7, 4, 1] >>> range(10, 0, -5) [10, 5]
Funkcijo range
uporabljamo skoraj izključno v zankah
for
.
V prvi zanki while
, ki smo jo napisali, smo izpisali števila
od 1 do 10, takole nekako:
i = 1 while i < 11: print(i) i += 1
Z zanko for
je preprostejši.
for i in range(1, 11): print(i)
Seveda ostaja odprto vprašanje, zakaj bi si kdo tako želel izpisati števila od 1 do 10. Štetje je kljub temu uporabno tudi za kaj drugega, ne le za izpisovanje.
Napisati želimo program, ki pove, ali je dano število praštevilo. Za to bomo
preverili vsa števila od 2 do n-1: če katerokoli od njih deli
n
, potem n
ni praštevilo.
n = int(input("Vpiši število")) je_prastevilo = True for i in range(2, n): if n % i == 0: je_prastevilo = False
V začetku predpostavimo, da je število praštevilo
(je_prastevilo = True
), če med števili med 2 in n
(for i in range(2, n)
) naletimo na njegov delitelj
(if n % i == 0
), pa presodimo, da število pač ne more biti
praštevilo (je_prastevilo = False
). Mimogrede naj opozorimo
na napako, na katero stalno opozarjamo:
n = int(input("Vpiši število")) je_prastevilo = True for i in range(2, n): if n % i == 0: je_prastevilo = False else: je_prastevilo = True # TO JE NAROBE!
Ko je število enkrat obsojeno kot sestavljeno, ga ne moremo več "pomilostiti"
nazaj v praštevilo. Če je tako, pa tudi ni potrebe, da bi po tem, ko enkrat
odkrijemo delitelj, sploh še preverjali naslednje potencialne delitelje.
Zdaj pač že vemo: break
in else
po zanki.
n = int(input("Vpiši število")) for i in range(2, n): if n % i == 0: print(n, "ni praštevilo, saj je deljivo z", i) break else: print(n, "je praštevilo")
for
code> in še malo telovadbeNekoč na začetku predavanja smo imeli seznam študentov in njihovih tež:
podatki = [ (74, "Anze", False), (82, "Benjamin", False), (58, "Cilka", True), (66, "Dani", False), (61, "Eva", True), (84, "Franc", False), ]
Recimo, da bi radi izpisali imean študentov in njihove teže.
Če gremo prek seznama z zanko for
, bomo dobivali terke. Te
lahko, vemo, razpakiramo.
for student in podatki: teza, ime, spol = student print(ime, ": ", teza)
Gre pa še dosti elegantneje: razpakiranje lahko opravimo kar znotraj
glave zanke, brez (nepotrebne) terke t
:
for teza, ime, je_zenska in podatki: print(ime, ": ", teza)
Program torej pravi
za vsako trojko (teza, ime, je_zenska) iz seznama podatki: izpisi ime in tezo
Lahko pa se tudi malo igramo in izrišemo graf:
for teza, ime, je_zenska in podatki: print(ime + " " + "*"*teza)
Skoraj, ampak ne čisto. Vse skupaj bi radi še poravnali. O tem, kako se v
resnici oblikuje izpis, se bomo pogovarjali drugič, danes pa bomo s tem
opravili nekoliko po domače.
Predpostavimo, da so imena dolga največ 15 znakov. Pred vsako ime bomo
dopisali toliko presledkov, da bo skupna dolžina enaka 15. (Mimogrede
bom izdal, da dolžino niza dobimo s funkcijo len
.)
for teza, ime, je_zenska in podatki: print(" "*(15 - len(ime)) + ime + " " + "*"*teza)
Zdaj pa izpišimo imena in teže, pri tem, da se le-ta nahajajo v ločenih seznamih.
teze = [74, 82, 58, 66, 61, 84] imena = ["Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc"] studentka = [False, False, True, False, True, False]
Naivno bi se lotili takole
for ime in imena: print(ime, ":")
in potem obstali, ker na tem mestu ne moremo priti do teže študenta s tem imenom. Do običajne in zelo zelo zelo napačne rešitve nas pripelje ta zgrešeni premislek: zanko moramo speljati prek imen in prek tež, torej
for ime in imena: for teza in teze: print(ime, ": ", teza)
Rezultat je nepričakovan za vse tiste, ki ga niso pričakovali. Program namreč izpiše
Anze : 74 Anze : 82 Anze : 58 Anze : 66 Anze : 61 Anze : 84 Benjamin : 74 Benjamin : 82 Benjamin : 58 Benjamin : 66 Benjamin : 61 Benjamin : 84 Cilka : 74 Cilka : 82 Cilka : 58 Cilka : 66 Cilka : 61 Cilka : 84 Daniel : 74 Daniel : 82 Daniel : 58 Daniel : 66 Daniel : 61 Daniel : 84 Eva : 74 Eva : 82 Eva : 58 Eva : 66 Eva : 61 Eva : 84 Franc : 74 Franc : 82 Franc : 58 Franc : 66 Franc : 61 Franc : 84
Računalniki imajo to nadležno navado, da vedno naredijo natanko tisto, kar jim naročimo. In v tem primeru smo mu naročili tole:
za vsako ime na seznamu ime stori tole: za vsako tezo na seznamu tez stori tole: izpisi ime in tezo
Kdor ne razume, kaj se je zgodilo in zakaj, naj to nujno premisli, da se ne bo kasneje učil na lastnih napakah.
Ko bo to opravljeno, bo razumel: potrebujemo samo eno zanko, ki gre istočasno po obeh seznamih. Aha, takole?
for ime in imena: for teza in teze: print(ime, ": ", teza)
Ummm, ne. Ena zanka je ena zanka. To sta dve. Običajni rešitvi tega problema
sta dve. To, ki bi bila večini bližje, bom danes zamolčal, iz vzgojnih
razlogov (kdor jo bo odkril sam, jo pač bo; žal). Funkcija zip
združi dva seznama (ali več seznamov) v seznam terk.
>>> zip(teze, imena) [(74, 'Anze'), (82, 'Benjamin'), (58, 'Cilka'), (66, 'Daniel'), (61, 'Eva'), (84, 'Franc')] >>> zip(teze, imena, studentka) [(74, 'Anze', False), (82, 'Benjamin', False), (58, 'Cilka', True), (66, 'Daniel', False), (61, 'Eva', True), (84, 'Franc', False)] >>> zip(teze, imena, studentka, teze) [(74, 'Anze', False, 74), (82, 'Benjamin', False, 82), (58, 'Cilka', True, 58), (66, 'Daniel', False, 66), (61, 'Eva', True, 61), (84, 'Franc', False, 84)]
Opomba: tudi tale izpis je iz starega Pythona. Od verzije 3.0 si
lahko le predstavljamo, da zip
dela takole. V resnici naredi
nekaj majčkeno drugačnega (kaj, je za nas ta mesec še prezapleteno),
rezultat pa je isti.
Zipamo lahko vse, prek česar lahko naženemo zanko for. Torej ne le seznamov, temveč tudi nize, terke in še kaj.
>>> zip("abcd", "ABCD") [('a', 'A'), ('b', 'B'), ('c', 'C'), ('d', 'D')] >>> zip("abcd", range(4)) [('a', 0), ('b', 1), ('c', 2), ('d', 3)]
Rešimo torej nalogo: izpišimo imena in teže iz ločenih seznamov.
for ime, teza in zip(imena, teze): print(ime, ": ", teza)
Za konec pa izpišimo še poprečno težo študentk.
skupna_teza = 0 studentk = 0 for teza, je_zenska in zip(teze, studentka): if je_zenska: skupna_teza += teza studentk += 1 print(skupna_teza / (studentk or 1))
Seznami, terke in naši stari znanci nizi imajo nekaj skupnega. Pravzaprav
veliko skupnega. Razlikujejo se le v podrobnostih. Skupno jim je, recimo,
da imajo vsi trije dolžino. Gornji seznami šestih tež, šestih imen in
šestih indikatorjev spola so dolžine 6. Terka 1, 2, 3, 4
je
dolžine 4 in niz "Benjamin" je dolžine 8. Dolžine reči, ki imajo dolžino,
nam pove funkcija len
. To sem napisal tako imenitno, da bom
moral še enkrat, da bo sploh kdo razumel: funkcija len
sprejme
en argument, recimo seznam, terko ali niz in kot rezultat vrne dolžino
tega seznama, terke ali niza.
>>> b = 'Benjamin' >>> len(b) 8 >>> len(podatki) 6 >>> len((1, 2, 3)) 3 >>> len(12) Traceback (most recent call last): File "", line 1, in TypeError: object of type 'int' has no len()
Morda je koga presenetilo zadnje: številka 12 bi lahko bila dolga 12, ne?
Ali pa 2, ker ima dve števki? Ne. Funkcija len
v bistvu pove
število elementov, ki jih vsebuje podani argument. Niz "Benjamin"
vsebuje 8 znakov, seznam podatki
vsebuje 6 podseznamov in
terka (1, 2, 3)
ima tri elemente. Število 12 pa nima
elementov.
Še ena prikladna značilnost seznamov (nizov, terk in še česa): prazni seznami so, tako kot prazni nizi, neresnični. Seznam lahko uporabimo v pogoju.
if s: print("Seznam s ni prazen") else: print("Seznam s je prazen")
Do elementov seznamov, terk in nizov ne pridemo le z zanko for. Dobimo jih lahko tudi tako, da preprosto zahtevamo element na tem in tem mestu. Vzemimo za primer seznam imen. Spomnimo se, kako je videti.
>>> imena ['Anze', 'Benjamin', 'Cilka', 'Dani', 'Eva', 'Franc']
Spremenljivka imena
vsebuje šest elementov. Če hočemo dostopati
do posameznega elementa - recimo izpisati njegovo vrednost - povemo njegov
indeks, "zaporedno številko". Zapišemo jo v oglate oklepaje za imenom
spremenljivke, takole:
>>> imena[2] 'Cilka'
Se pravi, imena[2]
vrne drugi element seznama.
Emmm, drugi?! Mar ni drugi element Benjamin, ne Cilka? Drži, ampak kakor šteje python, je drugi element Cilka. Anže je pa ničti. Ne le v pythonu, skoraj v vseh jezikih štejemo od 0. Prvi element ima indeks 0, drugi element 1 in tretji 2. Zakaj? Razlogi so tehnični in praktični. Tehničnega boste razumeli, ko boste (če boste) poslušali predavanja iz Cja. Tradicionalno indeks pomeni "odmik od začetka": ko je odmik 0, dobimo prvi element in ko je odmik 2, se odmaknemo dva elementa, torej pristanemo na tretjem. Medtem ko je v Cju (in še nižjih jezikih) ta, tehnični, argument morda smiseln, upravičimo štetje od ničle v Pythonu in drugih višjih jezikih s praktičnostjo: reči se tako lepše izidejo. Boste videli.
Če je koga zmedlo, povejmo na glas: oglati oklepaji imajo dve vlogi. Prej smo jih uporabljali, da smo vanje zaprli seznam, zdaj vanje zapiramo indekse. Naj vas to ne vznemirja, python bo že pravilno razumel, kaj mislite, celo v tako hecnih situacijah, kot je tale:
>>> [3, 1, 4, 1, 5, 9][2] 4
Prvi oklepaji definirajo seznam, drugi zaprejo indeks, 2, ki pove, kateri
element tega seznama nas zanima.
[
Zveni bedasto in neuporabno? Potem mi povejte, ali je bedasto in neuporabno
tole:
["moski", "zenska"][spol[0]]in tole
"MZ"[spol[0]]]
Na enak način kot sezname indeksiramo tudi terke in nize.
>>> 'Benjamin'[0] 'B' >>> 'Benjamin'[2] 'n'
Kadar seznam vsebuje sezname, bomo včasih uporabljali dvojne indekse.
Spomnimo se seznama podatki
: vsebuje šest elementov, in ti
elementi so spet seznami s po tremi elementi, ki predstavljajo težo, ime
in spol. Vzemimo, recimo, drugi element:
>>> en_student = podatki[2] >>> en_student [58, "Cilka", True]
Zdaj lahko o Cilki nekaj povemo.
>>> print(en_student[1], "tehta", en_student[0], "kilogramov.") Cilka tehta 58 kilogramov.
Lahko pa opravimo oboje v enem zamahu.
>>> print(podatki[2][1], "tehta", podatki[2][0], "kilogramov.")
podatki[2][0]
pomeni (če preberemo z desne) ničti element
drugega elementa seznama podatki
. Če se vam zdi to hudo, vas
lahko potolažim, da ni: v resnici je še bolj grozno. Ker je
podatki[2][1]
niz, se lahko vprašamo po, recimo, tretji črki
tega niza.
>>> print("Tretja crka niza", podatki[2][1], "je", podatki[2][1][3])
(V kost za glodanje C-jašem pa naj vprašam, ali
podatki[2][1][3][0][0][0][0]
kaj izpiše ali se pritoži, da je
tu nekaj narobe. Ampak to je pa res neuporabno.)
Kaj se zgodi, če je indeks prevelik? Nič lepega.
>>> "Benjamin"[100] Traceback (most recent call last): File "", line 1, in IndexError: string index out of range
Kako velik pa je lahko indeks? Če ima niz osem črk in je prva ničta, je
zadnja sedma. Indeks mora biti torej manjši od dolžine - največji dovoljeni
indeks je tisto, kar vrne funkcija len
, minus 1.
Python (in še marsikateri današnji jezik) ima še en trik: indeksiranje s konca: -1 je zadnji element, -2 predzadnji in tako naprej.
>>> 'Benjamin'[-1] 'n' >>> 'Benjamin'[-2] 'i' >>> 'Benjamin'[-3] 'm'
Poleg indeksiranja, ki vrača elemente nizov, seznamov, terk (in še česa), pozna Python še rezanje (slice), ki vrača dele nizov, seznamov, terk (in še česa). Rezino opišemo z indeksom prvega elementa in indeksom prvega elementa, ki ga ne želimo več vključiti v rezino. Med indeksa postavimo dvopičje. Se pravi, rezina 2:5 pomeni vse elemente od onega z indeksom 2 do tistega z indeksom 4 (ne 5!).
Smo to že kje videli? Smo, seveda. Funckija range
uporablja
natančno isto logiko. Tako kot pri range
, je tudi pri rezanju
to odlična ideja.
Oglejmo si, kaj na to pravi Benjamin.
>>> b[2:5] 'nja' >>> b[5:8] 'min' >>> b[2:5]+b[5:8] 'njamin'
Spodnjo ali zgornjo mejo smemo tudi izpustiti. V tem primeru dobimo vse elemente od začetka oz. do konca. Tu bosta izkazali svojo moč prav obe navidez neintuitivni pravili - štetje od 0 in to, da rezina ne vključuje zadnjega elementa.
>>> b[:5] >>> b[:5] 'Benja' >>> b[5:] 'min'
b[:5]
vrne prvih pet elementov. b[5:]
vrne vse
od petega naprej; ker štejemo od 0, to pomeni, da izpustimo prvih pet. Se
pravi, če želimo nizu s
odbiti prvih pet znakov, bomo rekli
s = s[5:]
Da peti študent vstane le enkrat, nam pride prav, če želimo v niz kaj vriniti. Če bi radi za petim znakom niza vrinili X, to storimo takole:
>>> b[:5]+"X"+b[5:] 'BenjaXmin'
Ste opazili, da nam sploh ni bilo treba pomisliti na to, da štejemo od 0? Vidite, kako naravno je to. Če bi šteli od 1, bi bilo tole precej bolj zapleteno.
Še dodatne možnosti prinese indeksiranje od zadaj. Iz niza lahko poberemo, recimo, elemente od predpredpredzadnjega (-5) do predzadnjega (-2). Koliko jih bo? Trije, seveda.
>>> b[-5:-2] 'jam'
No, tegale najbrž ne uporabimo velikokrat. Pač pa nas pogosto zanimajo, recimo, zadnji štirje znaki. Ali pa vsi razen zadnjih štirih.
>>> film = "Babylon 5 - 3x04 - Passing through Gethsemane.avi" >>> film[-4:] '.avi' >>> film[:-4] 'Babylon 5 - 3x04 - Passing through Gethsemane'
Mimogrede, tole sicer varneje delamo s funkcijo, ki smo jo že omenili,
namreč splitext
, ki prejme kot argument ime datoteke in vrne
terko z dvema elementoma, imenom datoteke brez končnice in končnico.
Kako pa bi od niza odbili prve tri in zadnja dva znaka? Takole:
>>> b[3:-2] 'jam'
Že tale primer pokaže, zakaj je štetje od 0 in čudno pravilo, po katerem je prvi element rezine vključen, zadnji pa ne, tako smiselno in uporabno. Tisti, ki bi v vsej stvari vendarle videli še kako logiko, pa bo morda prepričala spodnja tabelica, ob kateri lahko še enkrat premislijo vse primere.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |||||||||
B | e | n | j | a | m | i | n | ||||||||||
-8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
Vsi znaki med drugim in petim so 'nja', znaki od 3 do -2 so 'jam'... In tako naprej.
Vendar še nismo končali. Izpustimo lahko tudi zgornjo in spodnjo mejo. Kaj dobimo v tem primeru? Cel niz. Je to uporabno? Na nizih pravzaprav ne. Pri čem drugem pa nam bo prišlo še prav.
Poleg meja rezine lahko podamo tudi korak. Namesto vsakega znaka lahko zahtevamo, recimo, vsak drugi znak, tako da dodamo še eno dvopičje, ki mu sledi velikost koraka.
>>> '0123456789'[2:9] '2345678' >>> '0123456789'[2:9:2] '2468' >>> '0123456789'[2:9:3] '258' >>> '0123456789'[2:9:4] '26'
Korak je lahko, tako kot pri range
tudi negativen! V tem
primeru je potrebno zamenjati meji - prva mora biti višja od druge.
>>> '0123456789'[9:2:-1] '9876543' >>> '0123456789'[9:2:-2] '9753'
Meje smemo seveda spet tudi izpuščati:
>>> '0123456789'[9::-1] '9876543210'
Takole obrnemo predavatelja.
>>> 'demšar janez'[::-1] 'zenaj rašmed'
Vse tole je videti nekoliko zapleteno in tuje. In morda je: povaditi bo
treba, pa se bo udomačilo. Pa se splača? Za odbijanje znakov od nizov? Se!
Lepota je v tem, da se natanko enako kot indeksiranje in rezanje nizov
obnaša tudi indeksiranje seznamov in vsega drugega. Pa še kaj - zanke
for
, recimo, ki jih bomo vsak čas spoznali.
Poglejmo si torej še rezanje seznamov.
>>> imena = ["Anze", "Benjamin", "Cilka", "Dani", "Eva", "Franc"] >>> imena[2:5] ['Cilka', 'Dani', 'Eva'] >>> imena[2:] ['Cilka', 'Dani', 'Eva', 'Franc'] >>> imena[:2] ['Anze', 'Benjamin'] >>> imena[:-2] ['Anze', 'Benjamin', 'Cilka', 'Dani'] >>> imena[-2:] ['Eva', 'Franc'] >>> imena[::-1] ['Franc', 'Eva', 'Dani', 'Cilka', 'Benjamin', 'Anze']
Že videno. Rezanje seznamov se vede enako kot rezanje nizov. Rezanje terk pa prav tako.
Tole je preprosto. Vsak element seznama se vede na nek način kot spremenljivka: lahko mu priredimo vrednost in s tem "povozimo" prejšnjo vrednost.
>>> imena ['Anze', 'Benjamin', 'Cilka', 'Dani', 'Eva', 'Franc'] >>> imena[3] = "Daniel" >>> imena ['Anze', 'Benjamin', 'Cilka', 'Daniel', 'Eva', 'Franc']
Tako kot prej lahko tudi zdaj indeksiramo od spredaj ali od zadaj.
(Za tiste, ki znate kak drug jezik, predvsem Php: elementov ni mogoče
dodajati s prirejanjem! Pythonovi seznami niso ekvivalentni Phpjevim: temu,
česar se v phpju reče array
, je bližji pythonov slovar
(dictionary, dict
), ki ga bomo spoznali v
prihodnosti.)
Pa rezine? Glede na to, da je rezina podseznam, moramo tudi pri prirejanju rezin prirejati podsezname.
>>> imena ['Anze', 'Benjamin', 'Cilka', 'Daniel', 'Eva', 'Franc'] >>> imena[1:4]=["Ben", "Cecilija", "Dani"] >>> imena ['Anze', 'Ben', 'Cecilija', 'Dani', 'Eva', 'Franc']
Mora biti seznam, ki ga prirejamo, enako dolg kot rezina, ki jo bomo povozili? Ne, čemu? Takole zamenjamo tri z dvema:
>>> imena[1:4]=["Ben-Cecil", "Daniel"] >>> imena ['Anze', 'Ben-Cecil', 'Daniel', 'Eva', 'Franc']
Ali pa nobenega s tremi:
>>> imena[2:2] = ["D2", "D3", "D4"] >>> imena ['Anze', 'Ben-Cecil', 'D2', 'D3', 'D4', 'Daniel', 'Eva', 'Franc']
Z rezinami lahko tudi pobrišemo del seznama.
>>> imena ['Anze', 'Ben-Cecil', 'D2', 'D3', 'D4', 'Daniel', 'Eva', 'Franc'] >>> imena[2:5] ['D2', 'D3', 'D4'] >>> imena[2:5]=[] >>> imena ['Anze', 'Ben-Cecil', 'Daniel', 'Eva', 'Franc']
Za brisanje obstaja še veliko drugih načinov, recimo tale
>>> imena ['Anze', 'Ben-Cecil', 'Daniel', 'Eva', 'Franc'] >>> del imena[2] >>> imena ['Anze', 'Ben-Cecil', 'Eva', 'Franc'] >>> del imena[1:3] >>> imena ['Anze', 'Franc']
Če kdo pričakuje, da bomo zdaj povedali še, da enako delamo tudi z nizi in terkami ... se moti. Nizov in terk ne moremo spreminjati!
>>> b = 'Benjamin' >>> b[2]='a' Traceback (most recent call last): File "", line 1, in TypeError: 'str' object does not support item assignment
Tu je torej osnovna razlika med seznamom in terko: seznam je spremenljiv, terka ne. Tudi niza ne moremo spreminjati, kakor smo pravkar videli. Kako pa bi potem zamenjali tretji znak niza b s črko 'a'?
>>> b = b[:2]+'a'+b[3:] >>> b 'Beajamin'
To je seveda nerodno. Čemu je torej tako? Čemu ne moremo spreminjati nizov tako, kot sezname? (In čemu sploh ta trapasta terka?) Videli bomo, da nam pride včasih zelo zelo prav, da so nekateri objekti nespremenljivi. (Nekateri - pogosto tudi jaz - pravijo celo, da jeziki sploh ne bi smeli dopuščati spreminjanja spremenljivk.) Nize pa si v resnici redko želimo spreminjati - da, pogosto jih bomo sestavljali ali obtesovali, zelo redko pa si želimo spreminjati posamezne črke. Zato nas to, da so konstantni, ne bo preveč motilo, velikokrat pa nam bo koristilo.
V svojem prvem soočenju s programiranjem smo spoznali aritmetične izraze:
seštevali in množili smo števila, jih kvadrirali in računali sinuse.
Zadnjič smo naleteli na logične izraze, kjer smo računali z logičnimi
vrednostmi, True
in False
. Danes je čas, da se
nehamo čuditi ob vsakem izrazu posebej in jim dajati pridevke "aritmetični"
"logični" in tako naprej. Računati se da pač z različnimi rečmi in ena od
teh reči so tudi seznami.
Lahko sezname seštevamo? Kaj dobimo, če seštejemo [2, 5, -1]
in
[3, 7, 4]
? Dobimo [5, 12, 3]
ali
[2, 5, -1, 3, 7, 4]
? Oboje bi bilo smiselno, odgovor pa je
takšen: če so se seznami doslej vedli tako podobno nizom, naj se še glede
seštevanja.Ker je 'abc' + 'def'
enako 'abcdef'
,
naj bo tudi [2, 5, -1] + [3, 7, 4]
enako
[2, 5, -1, 3, 7, 4]
.
Podobno je z množenjem. [2, 5, -1] * 2
bi moralo biti po vsej
logiki enako [2, 5, -1] + [2, 5, -1]
, se pravi
[2, 5, -1, 2, 5, -1]
. In tudi je.
K seznamu lahko prištejemo le seznam, k nizu niz, k terki terko. Vse tri pa lahko pomnožimo s celim številom - in z ničemer drugim.
Poleg +
in *
pa poznajo vsi trije še dva
operatorja, in
in not in
. S prvim vprašamo, ali
seznam oz. terka vsebujeta določen element in ali niz vsebuje določen
podniz.
>>> 1 in [1, 2, 3] True >>> 4 in [1, 2, 3] False >>> 'in' in 'Benjamin' True >>> 'an' in 'Benjamin'
Drugi je seveda ravno nasproten, z njim se vprašamo ali seznam (niz) ne vsebuje elementa (podniza).
>>> 1 not in [1, 2, 3] False >>> 4 not in [1, 2, 3] True >>> 'in' not in 'Benjamin' False >>> 'an' not in 'Benjamin' True
V resnici not in
ni prav potreben, saj je
x not in l
isto kot not x in l
.