Seznami

Python ima podatkovni tip list. Jaz to prevedem v seznam, eni imajo raje tabela, ker se vede malo podobno kot array v kakšnem drugem jeziku.

teze = [74, 82, 58,
        66, 61, 84]
imena = [
    "Ana",
    "Berta",
    "Cilka",
]

Terke

Terka (tuple) je podobna seznamu, samo da jo zapremo v navadne oklepaje, t = (1, 2, 3).

Dolžina seznama, terke, niza

Funkcija len(s) vrne dolžino s. s je lahko karkoli, kar ima dolžino, npr. niz, seznam, terka, množica, slovar.

Razpakiranje v elemente

To je ena najboljših stvari v Pythonu (Kotlin jo delno imitira, JavaScript pa je tu še ful bolj kul).

Levo od enačaja je lahko več spremenljivk, če je desno od njega kaj, kar ima natančno toliko elementov. Če bi imeli

student = ("Ana", 65, "Ž")
prastevila = [2, 3, 5]
crke = "abcdef"

lahko pišemo

ime, teza, spol = student
prvo, drugo, tretje = prastevila
a, b, c, d, e, f = crke

Seveda lahko tudi

ime, teza, spol = ("Ana", 65, "Ž")

vendar je to čudno, in celo

ime, teza, spol = "Ana", 65, "Ž"

saj terk ni potrebno zapirati v oklepaje, vendar je to nečitljivo.

Pač pa je zanimivo

x = 1
y = 2
x, y = y, x
x
2
y
1

Prirejanje x, y = y, x je v bistvu isto kot x, y = (y, x), vendar to vedno pišemo brez oklepajev, ker je vsakemu jasno, da s tem menjamo vrednosti dveh spremenljivk. (Torej: sintakse x, y = y, x si niso izmislili za zamenjavo vrednosti spremenljivk, temveč je to slučajni stranski učinek tega, da terk ni potrebno zapirati v oklepaje in da lahko terke razpakiramo.)

Število imen levo od enačaja mora biti enako številu elementov stvari na desni. Lahko pa dodamo še spremenljivko, v katero gre višek. Označimo jo z zvezdico.

prvo, drugo, *ostala = [2, 3, 5, 7, 11, 13]
prvo
2
drugo
3
ostala
[5, 7, 11, 13]

Lahko celo

prvo, drugo, *ostala, zadnje = [2, 3, 5, 7, 11, 13]
ostala
[5, 7, 11]

Zvezdica nima zveze s kazalci, temveč prihaja iz drugega vica. To vlogo je prvič dobila ob klicih funkcij, ampak to bomo videli drugič.

Razpakiranje v elemente uporabljamo tudi zato, da navidez dosežemo, da lahko funkcija vrača več stvari. Funkcija splitext(fname), ki prejme ime datoteke ter vrne osnovni del in končnico, bi lahko bila definirana tako:

def splitext(fname):
    dot_idx = fname.rfind(".")
    base = fname[:dot_idx]
    ext = fname[dot_idx:]
    return base, ext

in bi jo klicali z

osnova, koncnica = splitext("en_film.avi")

V resnici return vrača return (base, ext), samo da tu ne pišemo oklepajev, ob klicu pa to razpakiramo nazaj. Tako sploh ni videti, da delamo s terkami, ampak je videti, kot da vračamo in dobimo dve stvari. Se pravi, v počasnem posnetku se zgodi to:

def splitext(fname):
    dot_idx = fname.rfind(".")
    base = fname[:dot_idx]
    ext = fname[dot_idx:]
    t = (base, ext)
    return t

in

ime_konc = splitext("en_film.avi")
osnova = ime_konc[0]
koncnica = ime_konc[1]

Zanka for

C-jevska zanka for je samo bolj kompaktno zapisan while. Zanka for(zacetek; pogoj; korak) { nekaj } je isto kot zacetek; while (pogoj) { nekaj; korak }. C ima torej dvakrat isto zanko.

Pythonova zanka for pa je drugačna od while. Je taka kot zanke, ki se ponekod for each ali kaj podobnega. Zanka for gre vedno čez neko zbirko stvari, recimo čez seznam, terko, niz, množico, slovar, vrstice datoteke, generator...

imena = ["Ana", "Berta", "Cilka", "Dani", "Ema"]
for ime in imena:
    print(ime)
Ana
Berta
Cilka
Dani
Ema

Prosim, ne pišite

for i in range(len(imena)):
    ime = imena[i]
    print(ime)

Razpakiranje v zanki for

Navadite se tako:

studenti = [("Ana", 65), ("Berta", 80), ("Cilka", 78)]
for ime, teza in studenti:
    ...

in celo

studenti = [("Ana", (65, 175)), ("Berta", (80, 180)), ("Cilka", (78, 160))]
for ime, (teza, visina) in studenti:
    ...

ne pa

for i in range(len(studenti)):
    ime = studenti[i][0]
    teza = studenti[i][1][0]
    visina = studenti[i][1][1]

Ker očitno.

V obeh zankah, for in while lahko uporabljamo break in continue.

Else po zanki

Zanki v Pythonu lahko sledi else. Kar zapišemo vanj, se izvede, če zanka ni bila prekinjena z break.

for x in stevila:
    if x % 2 == 1:
        print("Prvo liho število v seznamu je", x)
        break
else:
    print("Seznam ne vsebuje nobenega lihega števila")

Pozor: else je poravnan s for, ker se nanaša na zanko, in ne z if, kar bi bilo očitno narobe.

Zanka po dveh seznamih

Če imamo

imena = ["Ana", "Berta", "Cilka", "Dani", "Ema"]
teze = [72, 80, 68, 72, 67]

ne pišemo

for i in range(len(imena)):
    print(imena[i], teze[i])

temveč

for ime, teza in zip(imena, teze):
    print(ime, teza)
Ana 74
Berta 82
Cilka 58
Dani 66
Ema 61

zip vrne približno tole:

>>> zip(imena, teze)
[('Ana', 72), ('Berta', 80), ('Cilka', 68), ('Dani', 72), ('Ema', 67)]

Tako je bilo pred različico 3. V različici 3 vrne nekaj malo drugačnega, vendar se vede (skoraj) enako.

Funkciji zip lahko damo poljubno število seznamov in drugih stvari.

for ime, teza, crka, stevilka in zip(imena, teze, "abcdef", (1, 2, 3, 4, 5)):
    ...

Če argumenti nimajo enakega števila elementov, se zip ustavi, ko je konec najkrajšega.

Zanka prek številskega intervala

Pogosta rabe zanke for v C-ish jezikih je for(i = 0; i < 10; i++). V Pythonu je to for i in range(10). Lahko si predstavljamo, da range vrne seznam števil. V Pythonu 2.7 bi to bilo tako

>>> range(5)
[0, 1, 2, 3, 4]
>>> range(7, 12)
[7, 8, 9, 10, 11]
>>> range(10, 25, 4)
[10, 14, 18, 22]
>>> range(20, 10, -2)
[20, 18, 16, 14, 12]

Spodnja meja je vključena, zgornja ni, zato da range(a, b) vrne b - a elementov in zato da

>>> range(5, 8) + range(8, 10)
[5, 6, 7, 8, 9]

Od Pythona 3 naprej, range ne vrača seznamov, vendar vse deluje zelo podobno, kot da bi jih. Kaj vrača, izvemo čez par tednov.

Indeksiranje

Tole je namerno proti koncu, zato da se čimbolj navadite uporabljati for tako, kot je v Pythonu in podobnih jezikih treba.

"Običajni" indeksi

imena
['Ana', 'Berta', 'Cilka', 'Dani', 'Ema']
imena[0]
'Ana'
imena[1]
'Berta'

Indeksi s konca

Pogosto potrebujemo zadnji element. Ali predzadnjega. Ne pišemo imena[len(imena) - 1] ali imena[len(imena) - 2] temveč:

imena[-1]
'Ema'
imena[-2]
'Dani'

Rezine

S s[i:j] dobimo elemente seznama s od i-tega do j-tega. Z i-tim, a brez j-tega. Torej j - i elementov. Enako deluje tudi s terkami in nizi (Python nima nobenih substr in podobnih funkcij).

s = "Benjamin"
s[2:5]
'nja'

Če izpustimo spodnjo mejo, gre od začetka, tako da lahko s[:i] preberemo kar "prvih i elementov".

s[:3]
'Ben'

Če izpustimo zgornjo mejo, gre do konca. V bistvu s[i:] pomeni "brez prvih i".

s[3:]
'jamin'

Zdaj pa še negativni indeksi: s[:-i] pomeni vse do i-tega z desne, se pravi "brez zadnjih i".

s[:-3]
'Benja'

In s[-i:] je potem "zadnjih i elementov".

s[-3:]
'min'

Jasno delajo tudi kombinacije.

s[1:-1]
'enjami'

In še korak.

s[1:10:2]
'ejmn'

In celo

s[::-1]
'nimajneB'

s[:] vrne vse elemente od prvega do zadnjega, torej kopijo. To je uporabno predvsem za sezname. Terke in nizi so nespremenljivi, torej jih nima smisla kopirati.

Trik: zaporedni elementi

Če je s nek seznam, bomo z zip(s, s[1:]) dobili pare zaporednih elementov. Recimo, da nas zanima, ali števila v nekem seznamu naraščajo.

s = [2, 5, 8, 6, 10, 11, 15]

narascajo = True
for prej, potem in zip(s, s[1:]):
    if potem <= prej:
        narascajo = False

Še bolj pa mi je pravzaprav všeč

for prej, potem in zip(s, s[1:]):
    if potem <= prej:
        narascajo = False
        break
else:
    narascajo = True

Spreminjanje seznamov z indeksiranjem in rezanjem

imena
['Ana', 'Berta', 'Cilka', 'Dani', 'Ema']
imena[2] = "Cecilija"
imena[-1] = "Emilija"
imena
['Ana', 'Berta', 'Cecilija', 'Dani', 'Emilija']
imena[1:3] = ["Benjamin", "Boštjan", "Brane", "Cene"]
imena
['Ana',
 'Benjamin',
 'Boštjan',
 'Brane',
 'Cene',
 'Brane',
 'Cene',
 'Dani',
 'Emilija']
imena[5:] = []
imena
['Ana', 'Benjamin', 'Cene', 'Brane', 'Cene']

Vse to je možno početi le s seznami, ne pa tudi z nizi in terkami, saj so nespremenljivi.

Računske operacije na seznamih, nizih in terkah

Glede seštevanja se seznami in terke vedejo enako kot nizi: operator + stakne dva seznama oz. terki.

"Ana" + "marija"
'Anamarija'
[5, 7, 2] + [1, 8, 3]
[5, 7, 2, 1, 8, 3]

S seštevanjem si pomagamo tudi, če želimo spremeniti element terke ali niza.

s = "Be_jamin"
s = s[:2] + "n" + s[3:]
s
'Benjamin'

Tudi množenje seznamov se vede enako kot množenje nizov.

"Ana" * 3
'AnaAnaAna'
[5, 7, 2] * 3
[5, 7, 2, 5, 7, 2, 5, 7, 2]

Z operatorjem in (in not in) preverimo, ali seznam vsebuje (in ne vsebuje) določenega elementa.

5 in [5, 7, 2]
True
5 not in [5, 7, 2]
False

Za nize operator in preverja, ali vsebujejo podniz.

"min" in "Benjamin"
True
"min" not in "Benjamin"
False

Dodajanje elementov v seznam

imena
['Ana', 'Benjamin', 'Cene', 'Brane', 'Cene']
imena.append("Franz")
imena
['Ana', 'Benjamin', 'Cene', 'Brane', 'Cene', 'Franz']