## 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.

- Zapremo ga v oglate oklepaje. Vsebuje lahko elemente poljubnih tipov, lahko tudi mešanico tipov, npr. `["Ana", 74, True]`. Ampak tega ponavadi ne počnemo. Za različne tipe ponavadi uporabljamo terke (glej spodaj).

- Gre lahko čez več vrstic, če pustimo odprt oklepaj. Zamik v drugi in nadaljnjih vrsticah je načelno poljuben, vendar se splača lepo poravnavati.

    teze = [74, 82, 58,
            66, 61, 84]

- Za zadnjim elementom lahko naredimo vejico. To je praktično predvsem, če bomo, recimo, spreminjali vrstni red.

    imena = [
        "Ana",
        "Berta",
        "Cilka",
    ]

- `prazen` je zelo slabo ime za seznam. Če kdo napiše `prazen = []`, grem stavit, da ta seznam ne bo vedno prazen. Tudi `seznam` je slabo ime za seznam.

## Terke

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

- Oklepaje lahko tudi izpustimo: `t = 1, 2, 3`. Tega ponavadi ne počnemo.
- Če terka vsebuje samo en element, to ni `(42)` temveč `(42, )`. Če napišemo samo `(42)`, je to samo 42 v oklepaju.
- Pač pa je `()` čisto lepa prazna terka.
- V terko pogosto dajemo stvari različnih tipov.
- Bistvena in skoraj edina razlika med terkami in seznami je, da terk ne moremo spreminjati. Terke so za razliko od seznamov immutable.
- Python interno pogosto uporablja terke. Ko, recimo, pokličemo funkcijo, Python spakira vse argumente v terko in funkcija v resnici dobi terko. Samo da programer tega niti ne opazi, to je Pythonova privatna fora.

### 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']