<!DOCTYPE html>
<html lang="sl">
<head>
  <meta charset="utf-8" />
  <title></title>
  <link rel="stylesheet" type="text/css" href="/css/codeq.css" />
  <link rel="stylesheet" type="text/css" href="../../style.css" />
</head>
<body>

<h1>Kako uporabljamo module</h1>

<p>Doslej smo spoznali le prgišče funkcij - <code>input</code>,
    <code>len</code>, <code>sqrt</code>. V resnici pa skupaj s Pythonom dobimo
    tisoče funkcij za vse mogoče reči, od pošiljanja pošte in kriptografije do
    predvajanja zvoka in brskanja po datotekah .zip. Še večja gora funkcij
    obstaja na spletu: karkoli si zamislite napisati, skoraj gotovo je nekdo
    - če le gre za dovolj splošno reč - že potreboval kaj takšnega ter to tudi
    napisal in vam dal na razpolago. Da se ne bi izgubili v gori funkcij (in
    drugih reči), jih moramo nekako urediti.</p>

<p>Modul je zbirka funkcij (in omenjenih drugih reči). Veliko modulov dobimo
    že kar ob namestitvi Pythona, druge poberemo z interneta. Da bi lahko
    uporabljali funkcije iz modula, moramo modul najprej uvoziti, kar storimo
    z ukazom <code>import</code>, ki mu sledijo imena enega ali več modulov.
    S Pythonom dobimo, recimo, modul z matematičnimi funkcijami, ki se imenuje
    <code>math</code> in ga uvozimo takole:</p>

<pre>import math</pre>

<p>To praviloma storimo na začetku programa. Ko je modul uvožen, kličemo
    njegove funkcije. Do funkcije, ki se nahaja v modulu, pridemo tako, da
    napišemo ime modula, piko, in ime funkcije.</p>

<pre>>>> math.sqrt(2)
1.4142135623730951
>>> math.log(2.71828)
0.99999932734728203
>>> math.log10(100)
2.0</pre>

<p>Primer druge reči, ki je v modulu, so konstante.</p>

<pre>>>> math.pi
3.1415926535897931</pre>

<p>Drug primer modula je, recimo, modul s funkcijami, ki vračajo naključna
    števila (in počnejo tudi druge naključne reči),  <code>random</code>. Ta
    ima, recimo, funkcijo <code>randint(a, b)</code>, ki vrne naključno število
    med podanima vrednostima <code>a</code> in <code>b</code> (vključno z
    <code>b</code>! Tu pravilo o tem, da <code>b</code> ni vključen, ne bi
    imelo veliko smisla). Še dve zanimivi funkciji sta <code>choice</code>,
    ki vrne naključno izbrani element seznama, in <code>shuffle</code>, ki
    naključno premeša elemente seznama.</p>

<pre>>>> import random
>>> random.randint(10, 20)
17
>>> random.randint(10, 20)
16
>>> random.randint(10, 20)
10
>>> l = ["Ana", "Berta", "Cilka", "Dani", "Ema", "Fanci"]
>>> random.choice(l)
'Ana'
>>> random.choice(l)
'Dani'
>>> random.shuffle(l)
>>> l
['Ana', 'Fanci', 'Dani', 'Cilka', 'Ema', 'Berta']</pre>

<p><code>random.gauss(mu, sigma)</code> vrača naključna števila iz Gaussove
    distribucije s podanim poprečjem <code>mu</code> in varianco
    <code>sigma</code>. Tule je pet števil iz distribucije N(10, 3):</p>

<pre>>>> for i in range(5):
... 	print(random.gauss(10, 3), end=" ")
...
8.96174816507 8.79299353551 8.75687382602 9.49106109252 8.21589651224</pre>

<p>Poglejmo še en modul, <code>os</code>. Ta vsebuje goro funkcij, povezanih
    z datotekami in direktoriji (podatki o datotekah, preimenovanje, brisanje),
    programi, ki tečejo na računalniku in drugo. Tako recimo funkcija
    <code>os.listdir</code> vrne seznam vseh datotek v direktoriju, katerega
    ime podamo kot argument.</p>

<pre>>>> os.listdir("/Users/janez/Dropbox/Pedagosko/P1/2014/03 seznami")
['blagajna.py', 'palindrom.py', 'stetje.py', 'domaca', 'razpakiranje.py',
'zapiski.html', 'fibo.py', 'seznami.py', 'zip.py']</pre>

<p>Funkcija <code>os.remove</code> pobriše datoteko s podanim imenom. Če bi
    želeli v direktoriju <code>c:\d\kontrabant</code> pobrisati vse datoteke s
    končnico <code>.pyc</code>, bi to storili takole:</p>

<pre>for fname in os.listdir("/Users/janez/Dropbox/Pedagosko/P1/2014/03 seznami"):
    if fname[-4:] == ".py":
        os.remove("/Users/janez/Dropbox/Pedagosko/P1/2014/03 seznami" + fname)</pre>

<p>Ob tej priliki povejmo, da ima v normalnih operacijskih sistemih vsak program
    (bolj učeno: proces) nek "trenutni direktorij". Kadar imena direktorija
    ne začnemo z / (ali \ ali c: ali čim podobnim na Windowsih), se ime nanaša
    na datoteke oz. direktorije znotraj tega, trenutnega direktorija. Kakšen je
    trenutni direktorij, nam pove funkcija <code>getcwd()</code> (get current
    working directory), spremenimo pa ga z <code>os.chdir</code>. Kar smo
    počeli zgoraj, bi se lahko napisalo tudi

<p>Vse skupaj bi bilo morda lepše, če bi predtem zamenjali trenutni direktorij.</p>

<pre>os.chdir("/Users/janez/Dropbox/Pedagosko/P1/2014/03 seznami")
for fname in os.listdir("."):
    if fname[-4:] == ".py":
        os.remove(fname)</pre>

<p>Ko smo ravno pri Windowsih, potarnajmo še, da ima znak \ v nizih v večini
    programskih jezikov poseben pomen. Danes povejmo le, da moramo, če hočemo
    dobiti \, napisati \\. Se pravi, ko bomo hoteli reči "c:\Users\janez", bomo
    morali napisati "c:\\Users\\janez". Na srečo lahko tudi na Windowsih že
    dolgo uporabljamo sicer običajnejši /.</p>

<p>Če bi datoteke raje preimenovali kot brisali - recimo tako, da bi k njihovem
    imenu dodali .bak, to storimo z <code>os.rename</code>, ki ji podamo staro
    in novo ime datoteke.</p>

<pre>for fname in os.listdir("."):
    if fname[-4:] == ".py":
        os.rename(fname, fname + ".bak")</pre>

<p>Ob modulu <code>os</code> se lahko naučimo še nečesa zanimivega: modul
    lahko vsebuje celo druge module. Tako modul <code>os</code> vsebuje modul
    <code>path</code>. Modula <code>path</code> nam ni potrebno uvoziti,
    dovolj je, da uvozimo <code>os</code>. Lahko pa ga uvozimo tudi takole</p>

<pre>import os.path</pre>

<p>Razlike pravzaprav (skoraj) ni.</p>

<p>Modul <code>os.path</code> vsebuje različne funkcije povezane z imeni
    datotek in njihovimi lastnostmi. Zanimiva je, denimo,
    <code>os.path.splitext</code>, ki ji podamo ime datoteke in vrne terko z
    dvema elementoma - osnovo imena in končnico. Pri tem ni potrebno, da
    datoteka v resnici obstaja.</p>

<pre>>>> os.path.splitext("/Users/janez/datoteka.txt")
('/Users/janez/datoteka', '.txt')</pre>

<p>Ker vemo, da lahko terko razpakiramo v dve spremenljivki, bomo pogosto rekli
    kar</p>

<pre>>>> osnova, koncnica = os.path.splitext("/Users/janez/datoteka.txt")</pre>

<p>Gornji programček bi se torej še bolj lepo napisalo takole.</p>

<pre>for fname in os.listdir("."):
    if os.path.splitext(fname)[1] == ".py":
        os.rename(fname, fname + ".bak")</pre>

<p>Modul <code>os.path</code> ima še kup zanimivih funkcij, recimo
    funkcijo, ki pove, ali določena datoteka oz. direktorij obstaja
    (<code>os.path.exists</code>), funkcije, ki povedo ali določeno ime
    predstavlja datoteko (<code>os.isfile</code>), direktorij
    (<code>os.isdir</code>), povezavo (<code>os.islink</code>) ali kaj
    drugega, kar je znano na Unixu, v Windowsih pa ne.</p>

<h2>Uvažanje v globalni imenski prostor</h2>

<p>Kaj je "imenski prostor" vam še ne nameravam, kaj "globalni", pa še ne
    morem povedati. Vseeno se lahko naučimo, kako uvažamo vanj.</p>

<p>Stvar je namreč jako preprosta. Morda se mi ne da stalno pisati
    <code>math.sin</code>, <code>math.cos</code>, <code>math.pi</code>. Če bi
    raje pisal le <code>sin</code>, <code>cos</code> in <code>pi</code>, bom
    namesto z

<pre>import math</pre>

<p>modul uvozil z</p>

<pre>from math import sin, cos, pi</pre>

<p>S tem povem dve reči. Prva: zanimajo me le tri reči, <code>sin</code>,
    <code>cos</code> in <code>pi</code>. Ostalih, recimo <code>sqrt</code>,
    ne potrebujem in naj jih ne uvaža. (Dejanski mehanizem je malo drugačen,
    tudi <code>sqrt</code> je v resnici uvožen, vendar ga ne vidim, a to ni
    pomembno.) Druga: nočem, da se funkcije "skrivajo" v modulu
    <code>math</code>, hočem jih "tu", torej, hočem jih klicati, ne da bi moral
    prej pisati <code>math.</code>.</p>

<p>Pogosto smo leni ali, še pogosteje, ne vemo točno, katere vse funkcije
    bomo potrebovali, zato rečemo kar</p>

<pre>from math import *</pre>
<p>Zvezdica pomeni "vse funkcije" in hočemo jih tu, ne v <code>math</code>.
    Za to obliko uvoza sem vam, da nam je bilo enostavneje, pokazal, ko smo
    začeli streljati s topom.</p>

<p>Temu načinu uvažanja se načelno izogibamo. Pogosto ga uporabljamo pri modulu
    <code>math</code>, iz drugih modulov pa uvažamo le posamične funkcije
    (<code>from random import randint</code>) ali pa pustimo modul kot modul
    (<code>import random</code>).</p>

<h1>Kako pišemo module</h1>

<p>Napišimo program, ki bo vseboval konstanto <code>odgovor</code>,
ki bo imela vrednost 42, in funkcijo, ki računa Fibonaccijeva števila.

<pre>odgovor = 42

def fibonacci(n):
    a = b = 0
    for i in range(n):
        a, b = b, a+b
    return a</pre>
Program shranimo pod imenom fibo.py.</p>

<p>To je to. V drugem programu lahko rečemo

<pre>import fibo
print("Odgovor je", fibo.odgovor)
print("Deseto Fibonaccijevo število pa je", fibo.fibonacci(10))</pre>

<p>Tudi vse ostale finte, na primer, <code>from fibo import odgovor</code>,
    delujejo.</p>

<p>Modul ni nič drugega kot program, ki ga uvozimo.</p>

<p>Tudi vsi drugi programi, ki ste jih napisali doslej, so hkrati moduli:
    lahko jih uvozite. Pazite le na tole: ko modul uvozimo,
    se ta v resnici izvede, čisto tako, kot bi se izvajal program.
    Vse, kar se v tem programu-modulu definira, ostane definirano in se
    nahaja v modulovem imenskem prostoru. Vendar se zgodi tudi vse ostalo,
    kar piše v modulu: če pri izvajanju naleti na <code>print("Foo")</code>
    se bo ob uvozu modula izpisalo Foo. Če program-modul vsebuje klic funkcije
    (in ne le definicij), se bo ta funkcija poklicala tudi ob uvozu.</p>

<p>Kje Python išče module? Navadno v trenutnem direktoriju,
    poleg tega pa še v drugih. Na Windowsih v, recimo,
    <code>c:\Python34\lib\site-packages</code>.
    Več o tem si lahko preberete v dokumentaciji.</p>

<p>Tudi sicer ne bomo rinili prav globoko v module,
    samo še eno stvar omenimo, da vas ne bo presenetila.
    Ko boste prvič uvozili modul, se bo poleg datoteke s končnico .py
    pojavila še ena, za enakim imenom, a končnico .pyc.
    Python si vanjo "prevede" (pravzaprav bi smeli pisati brez narekovajev,
    temu se v resnici reče prevajanje) v obliko, v kateri ga bo lahko
    naslednjič hitreje uvozil. Pri manjših modulih se to ne pozna,
    pri velikem številu velikih modulov pa. Če vas moti, jo lahko pobrišete;
    drugič se bo pač spet pojavila ponovno.</p>

</body>
</html>