summaryrefslogtreecommitdiff
path: root/python/problems/functions_and_modules/modules_sl.html
blob: ec414efbaaa13287fc43e4f5d25dbf7fee963f83 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
<!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>