V prejšnjem besedilu smo spoznali osnovna načela jezika Python (http://www.python.org/). Spoznali smo, da gre za tolmačen jezik, s pomočjo ustreznih orodij pa lahko za okolje Windows naredimo tudi izvršljivo datoteko. S pomočjo predstavljenih zgledov smo naredili prve korake v svet Pythona in spoznali nekatera njegova pravila in zakonitosti. Kot že omenjeno, uporabimo ga lahko v različnih okoljih (Windows, Linux … okolju ukazne vrstice ali grafičnem vmesniku) kot samostojno aplikacijo ali v vlogi »lepila«, ki povezuje druge aplikacije.

V do zdaj opisanih primerih smo spoznali pravila pisanja Pythonove kode (na primer potrebni zamiki) ter osnovna pravila spremenljivk in razlike med različicami Pythona. Ogledali smo si in spoznali nekatere najpogosteje uporabljene stavke (for, while, if …) ter način, kako lahko že pripravljene module uporabimo v lastnih »izdelkih« in z njihovo pomočjo »razširimo« funkcionalnost. Pregled nad nekaterimi najpogostejšimi moduli (za različico 3.3) lahko najdemo na spletni strani http://docs.python.org/3.3/py-modindex.html. Do opisa modulov za druge različice pa pridemo s spremembo različice v izbranem polju. Da ne bo pomote, na seznamu še zdaleč niso vsi moduli … K njim se bomo vrnili nekoliko pozneje.

ZAPIS V DATOTEKO

Do zdaj smo spoznali, kako vnesemo vrednost določene spremenljivke in jo izpišemo na zaslonu. Če smo našo Pythonovo lupino zapustili ali izvedli njen ponovni zagon, so se vnesene vrednosti izgubile. Če želimo to vrednost shraniti, lahko to storimo s pomočjo vpisa v datoteko. Najprej pa si poglejmo, kako iz datoteke beremo. V imeniku, na katerem imamo nameščen Python, s pomočjo urejevalnika (na primer beležnice) naredimo tekstovno datoteko z imenom mojmikro.txt in vanjo vpišimo poljubno besedilo. V našem primeru smo napisali To je besedilo testne datoteke in datoteko shranili. V Pythonovi lupini zdaj vnesemo vrstice:

b=open("mojmikro.txt","r")
a=b.read()
print(a)

S prvo vrstico odpremo datoteko mojmikro.txt za branje (parameter »r«). Parametru »r« bi lahko bil dodan tudi parameter »b«, ki označuje binarno datoteko. Z drugo vrstico smo prebrali vsebino datoteke mojmikro in jo priredili spremenljivki a. Zadnja vrstica pa je izpisala vrednost spremenljivke (oziroma posredno tekstovne datoteke) besedila To je besedilo testne datoteke. Datoteko bi lahko uporabili tudi s pomočjo ukaza file. Primer branja datotek pa je praktično enak:

b=file("mojmikro.txt","r")
a=b.read()
print(a)

Zaženimo zdaj ponovno Pythonovo lupino in vnesimo vrstice:

a="mojMikro"
b=file("zapis.txt","w")
b.write(a)
b.write(" acb123")
b.close()

S prvo vrstico priredimo spremenljivki a vrednost mojMikro. V naslednji odpremo v Pythonovem imeniku za zapisovanje datoteko zapis.txt (ker datoteke še nimamo, ustvari ta ukaz novo datoteko). V to tekstovno datoteko zapišemo vrednost spremenljivke »a«, ki ji dodamo še niz »abc123«. Z zadnjo vrstico datoteko zapremo. Učinek lahko preverimo s pomočjo urejevalnika besedil.

Zgled nadaljujmo z vrsticami:

b=file("zapis.txt","w")
b.write(" nov zapis")
b.close()

in preverimo vsebino datoteke zapis.txt. Ugotovimo, da je ta akcija nadomestila oziroma prepisala besedilo mojMikro abc123. Če bi želeli združiti vse vnose, bi morali uporabiti parameter »a« (append – dodaj), zato bi morala biti prva vrstica videti b=file("zapis.txt","a"). Pazljivi moramo biti, ali v datoteko določene vrednosti zapisujemo ali dodajamo.

SEZNAMI …

Drugi pogost način zapisovanja oziroma shranjevanja različnih vrednosti, ki ga lahko uporabimo, so različni seznami. Na seznam lahko vpišemo najrazličnejše tipe – na primer števila, znake, logične vrednosti … Seznam definiramo znotraj oglatega oklepaja in je videti tako:

mojmikro = [“marko“,“torek“,1234,4321,”13579“]

nato vnesimo še vrstici:
mojmikro[0]
mojmikro[1]

ki nam izpišeta 'marko' in 'torek'. Kot lahko vidimo, so elementi seznama indeksirani, zato lahko do vsakega elementa seznama pridemo z navajanjem njegovega indeksa. Indeksiranje se začne s številom »0«, to vrednost pa ima prvi element (na levi strani!).

Poskusimo z vnosom mojmikro[2]+mojmikro[3]. Dobimo pričakovan rezultat 5555 kot rezultat vsote elementov seznama mojmikro (števil 1234 in 4321). S preprosto operacijo lahko ugotovimo število elementov našega seznama. To storimo s pomočjo ukaznega niza len(mojmikro), ki nam izpiše vrednost 5. Nadaljujmo naše delo s seznamom in vnesimo ukaz mojmikro[-1]. Ker vemo, da se indeksiranje začne s številom »0«, bi pričakovali, da nas bo Pythonova lupina opozorila na napako. Rezultat pa je izpis »13579«, ki je zadnji element našega seznama. Če vnesemo še mojmikro[-2], dobimo izpis števila 4321, s čimer smo ugotovili, da vrednost negativnega indeksa pomeni izpis elementov seznama z desne strani. Poskusimo še z vrstico:

mojmikro[1:3]

Na podlagi do zdaj pridobljenega znanja lahko pričakujemo izpis elementov seznama z indeksi od 1 do 3. Rezultat pa se od pričakovanega razlikuje, saj je izpis:

['torek', 1234]

S seznama smo dobili le vrednosti z indeksom 1 (torek) in indeksom 2 (1234). Vrednosti z indeksom 3 pa ni. Razlog za to je preprost. Python vedno izpiše vrednost elementa z indeksom spodnje meje, elementa z indeksom zgornje meje pa ne.

Vrednosti lahko v naš seznam tudi dodajamo. Eden od načinov dodajanja novega elementa je, da tega dodamo na mesto, ki še ni zasedeno. To storimo s pomočjo vnosa:

mojmikro.append("Erik")
po izpisu (mojmikro + enter) je nov seznam videti takole:

['marko', 'torek', 1234, 4321, '13579', 'Erik']

Ker se je zaradi določenih okoliščin spremenil dan, lahko vrednost »torek« spremenimo v nedeljo s pomočjo niza:

mojmikro[1]="nedelja"

nov seznam pa je po pričakovanju ['marko', 'nedelja', 1234, 4321, '13579', 'Erik']. Ker elementov seznama – števil 1234 in 4321 – ne potrebujemo več, jih izbrišemo s pomočjo ukaza del mojmikro[2:4]. Tudi tokrat ni bila upoštevana zgornja meja, zato je naš seznam po novem ['marko', 'nedelja', '13579', 'Erik']. Če vrednosti indeksa iz takšnega ali drugačnega razloga ne poznamo, lahko element seznama izbrišemo (v našem primeru 13579) tudi s pomočjo ukaza remove in navajanjem elementa. V našem primeru bi to pomenilo mojmikro.remove("13579"). Na našem seznam se je tako skrčil le na tri elemente in je zdaj ['marko', 'nedelja', 'Erik'].

Ustvarimo nov seznam test/testni seznam, ki ga definiramo kot:
test=[8,4,1,2,9,1,3,7,6,7,7,10]

Brez štetja lahko pridemo do skupnega števila elementov seznama – s pomočjo ukaza len(test) ugotovimo, da ima naš seznam dvanajst elementov. Elemente seznama želimo razvrstiti po velikosti. Razvrščanje lahko izvedemo na dva načina. Prvi je, da uredimo sam seznam, drugi pa, da ostane osnovni seznam tak, kot je, rezultat razvrščanja pa je nov seznam. Oglejmo si najprej prvo možnost. Razvrščanje samega seznama izvedemo s pomočjo ukaza test.sort(). Če izpišemo zdaj seznam test, dobimo števila, urejena po velikosti [1, 1, 2, 3, 4, 6, 7, 7, 7, 8, 9, 10]. Vrednosti lahko izpišemo tudi v obratnem vrstnem redu, s pomočjo ukaza test.reverse(). Druga možnost pa bi bila, da definiramo na podlagi tega ukaza nov seznam (na primer x), ki ga definiramo kot test (in tako dobimo delovno kopijo).
Hitro lahko preštejemo tudi, koliko imamo določenih elementov na našem seznamu. Tako nam niz test.count(1) izpiše število 2 (imamo dve števili 1), ukaz test.count(7) pa število 3 (na seznamu imamo trikrat navedeno število 7). Jasno je, da nam test.count(5) vrne vrednost »0«, saj nimamo nikjer na seznamu elementa – števila 5. Vsa števila v našem seznamu lahko tudi seštejemo s pomočjo ukaza sum(test) – dobimo rezultat 65, ugotovimo minimalno vrednost s pomočjo niza min(test) – dobimo vrednost 1, ugotovimo maksimalno vrednost s pomočjo ukaza max(test) – ki nam vrne vrednost 10. Povprečno vrednost števil našega seznama lahko tudi preprosto izračunamo s pomočjo sum(test)/len(test)…

Na tem mestu se dotaknimo še logičnih vrednosti. Resnično trditev označuje »True«, trditev, ki ne drži, pa »False«. Primerjanje se izvaja paroma. Če na enem od seznamov zmanjka elementov, je seznam pač krajši.

… IN SLOVARJI

Podobna in uporabna struktura za zapis (kot seznami) so slovarji. S pomočjo slovarjev lahko hitro dostopamo do posameznih elementov. Strukturo slovarja spoznamo po zavitih oklepajih, v katerih najdemo pare tako imenovanih ključev in pripadajočih vrednosti. Definirajmo naš slovar z imenom slovar, v katerem definiramo pare:

slovar={"uporabnik1":"Marko","E-naslov1":"marko@mojmikro.si","uporabnik2":"Janez","E-naslov2":"janez@mojmikro.si","tel1":"0311234567", "tel2":"0407654321"}

Takšen zapis je sicer pravilen, ni pa ravno pregleden. Uporabniku prijaznejši, predvsem pa preglednejši bi bil zapis, ki jasno kaže povezavo ključev in vrednosti:
slovar={"uporabnik1":"Marko",
"E-naslov1":"marko@mojmikro.si",
"uporabnik2":"Janez",
"E-naslov2":"janez@mojmikro.si",
"tel1":"0311234567",
"tel2":"0407654321"}

S slovarji delamo podobno kot s seznami, le da v primeru seznamov vrednosti iščemo z indeksi, v primeru slovarjev pa s ključi. Tako nam vrstica slovar["tel1"] kot odgovor izpiše vrednost '0311234567'. Ključe, ki jih uporabljamo v našem slovarju, lahko izpišemo s pomočjo ukaza slovar.keys(). Nov vnos pa bomo v naš slovar dodali z vrstico (oglati oklepaji!) slovar["novkljuc"]="vrednost_X"], ki nam obstoječ slovar spremeni v:

{'novkljuc': 'vrednost_X', 'uporabnik1': 'Marko', 'tel2': '0407654321', 'E-naslov2': 'janez@mojmikro.si', 'E-naslov1': 'marko@mojmikro.si', 'tel1': '0311234567', 'uporabnik2': 'Janez'}

V naslednjem koraku želimo vrednost novkljuc spremeniti iz vrednost_X v vrednost_Y. To naredimo tako, da obstoječo vrstico prepišemo z ukaznim nizom slovar["novkljuc"]="vrednost_Y"]. Na pričakovan način pa lahko določen ključ (novkljuc) tudi izbrišemo – del slovar["novkljuc"].

Zaradi prepoznavanja omenimo še »terko«, ki je neke vrste seznam – »terko« prepoznamo po zavitem oklepaju. »Terko« uporabljamo podobno kot seznam (indeksiranje), v nasprotju s seznamom pa so njene vrednosti fiksne.

ŠE PA ŠE – MODULI

Uporabo modulov smo precej na hitro spoznali že v pretekli številki. Spoznali smo način uvoza modula, ki nam omogoča uporabo že pripravljenega modula v naših rešitvah. Uvozimo Pythonovo lupino modul calendar s pomočjo ukaza import calendar. Z vnosom niza calendar.prcal(letnica) dobimo izpis koledarja za celotno leto, ki smo ga vnesli v polje letnica. Če pa bi uvozili modul z ukaznim nizom from calnedar import *, bi koledar za želeno leto izpisali z ukazom prcal(letnica) – na primer prcal(1992). Podobno kot pri drugih modulih dobimo izpis možnih »parametrov« tako, da izpišemo calendar (nato pritisnemo tipko TAB) in izbiramo med ponujenimi možnostmi. Tako najdemo recimo calendar.prmonth, ki nam s parametrom (2013,9) izpiše koledar za september.

Več o možnostih, ki jih določen modul ponuja, dobimo s pomočjo ukazne vrstice in ukaza help (ime_modula) – na primer help(calendar). Sorodna modula sta modula time in datetime. Njuno uporabo omogočimo z ukazom import time in import datetime. Ukazni niz time.localtime() nam izpiše informacijo o trenutnem datumu in času v obliki:

time.struct_time(tm_year=leto, tm_mon=mesec, tm_mday=dan, tm_hour=ura, tm_min=minute, tm_sec=sekunde, tm_wday=dan v tednu (ponedeljek=0), tm_yday=dan v letu, tm_isdst=0/1)

Verjetno pa bo večini ljubša nekoliko krajša oblika, ki jo dobimo z ukazom time.asctime(), ki nam izpiše recimo 'Sat Jun 8 16:55:15 2013'.

Pogosto uporabljamo tudi modul random. Ena od njegovih možnosti je generiranje naključnih števil. Ukazni niz random.randint(1,6) nam generira naključno celo število med 1 in 6. Jasno je, da lahko že v nekaj vrsticah napišemo preprost program, ki nam simulira metanje kocke.

print ("Metanje kocke")
from random import *
a=0
while a<1:
input("Pritisni tipko Enter ")
x=randint(1,6)
print ("Na kocki je", x)

Za dostop do specifičnih parametrov in funkcij uporabljamo modul sys. Omenimo le nekaj možnosti. Ukaz path nam izpiše poti definiranih imenikov, ukazni niz getwindowsversion() pa ugotovi našo različico operacijskega sistema, v katerem imamo nameščeno našo različico Pythona. Če želimo ugotoviti vrsto platforme, lahko to storimo s pomočjo ukaza platform.

Omenili smo že, da lahko Python uporabljamo v vlogi povezovalnega elementa med različnimi aplikacijami. Če želimo iz Pythona pognati določeno (zunanjo) aplikacijo, to storimo s pomočjo ene od možnosti modula os. Modul naložimo z ukazom import os. Beležnico bomo poklicali iz Pythonove lupine s pomočjo vrstice os.system("notepad.exe"). Pogosto je koristna tudi uporaba modula scoket. Z njegovo pomočjo lahko preprosto dobimo želeno informacijo, na primer IP-naslov strežnika, ki ga priredimo določeni spremenljivki a=socket.gethostbyname("www.mojmikro.si") -> a= '91.185.204.85'. Do zdaj pridobljeno znanje preverimo ob razumevanju zgleda s spletne strani http://docs.python.org/3.3/library/email-examples.html.

# smtplib nalaganje modula – SMTP odjemalec – opis odjemalca na strani
# http://docs.python.org/3.3/library/smtplib.html?highlight=smtplib#smtpli...
import smtplib

# nalaganje komponente drugega modula – opis modula na strani
# http://docs.python.org/3.3/library/email.mime.html
from email.mime.text import MIMEText

# Odpiranje datoteke textfile - samo ASCII characters.
fp = open(textfile, 'rb')
# Priprava sporočila v prostem tekstu
msg = MIMEText(fp.read())
# Zapiranje datoteke
fp.close()

# me == naslov posiljatelja
# you == prejemnikov naslov
msg['Subject'] = 'Vsebina %s' % textfile
msg['From'] = me
msg['To'] = you

# Pošiljanje sporočila preko lokalnega SMTP strežnika.
s = smtplib.SMTP('localhost')
s.send_message(msg)
s.quit()

Kot lahko vidimo, smo »uporabili« v večini primerov do zdaj pridobljeno znanje (uvažanje in uporaba modulov, odpiranje in zapiranje datoteke). Osebno se držim načela, da se je (če že) upravičeno kititi z lastnim perjem, a pri programiranju je nesmiselno odkrivanje tople vode, saj z uporabo že narejenih (in preizkušenih) modulov običajno prihranimo veliko dela in časa. Zato je smiselno, da pred morebitnim pisanjem preverimo, ali morda že ni na voljo modul, ki nam omogoča želeno funkcionalnost.

ŽELVANJE ZA MLAJŠE

Dejstvo je, da veliko mladih tehnična ozadja (in zato tehnični poklici) ne zanimajo. Eden od vzrokov naj bi bil tudi ta, da vse skupaj ni zabavno. Pogosto so prvi koraki v svet računalništva pri otrocih povezani z risanjem, ki pa je lahko zelo zabavno. Zato je dobrodošlo, da Python ponuja želvo (turtle), ki jo poznamo tudi iz programskega jezika Logo in je namenjena učenju programiranja za otroke.

Po pričakovanju »oživimo« želvo z uvozom modula turtle. Ogledali si bomo nekaj preprostih zgledov, predlagam pa vam, da si ogledate opis možnosti modula (help(turtle)), saj boste ugotovili, da gre za zmogljivo orodje, ki omogoča marsikaj.

Začetno okno odpremo po uvozu modula (import turtle) s pomočjo ukaznega niza zelva=turtle.Pen(). Na zaslonu se nam odpre novo okno z »želvo« (v resnici puščico) na sredini, ki je neke vrste koordinatno izhodišče (0,0). Površina, po kateri se bo naša želva lahko gibala, je definirana s točkami, po katerih se bomo gibali tako kot pri grafih (koordinatni sistem). Če želimo narisati črto z dolžino 100 točk, storimo to s pomočjo ukaza zelva.forward(100). To črto lahko nadaljujemo v izris stranic kvadrata, zato nadaljujemo risanje navzgor. Našo želvo moramo zato preusmeriti za 90 stopinj v levo. To storimo s pomočjo ukaza zelva.left(90). Smer želve kaže navzgor, zato lahko nadaljujemo z risanjem in ponovimo ukaz zelva.forward(100). Postopek obračanja želve ponovimo in nadaljujemo z risanjem, dokler kvadrat ni končan. Za celotno risanje kvadrata smo morali vnesti:

import turtle
zelva=turtle.Pen()

zelva.forward(100)
zelva.left(90)
zelva.forward(100)
zelva.left(90)
zelva.forward(100)
zelva.left(90)
zelva.forward(100)

Želvo lahko premikamo tudi v obratni smeri puščice. Želeni premik za 200 točk nazaj naredimo s pomočjo ukaza zelva.backward(200). V našem primeru to pomeni, da se pero vrača prek obstoječe črte stotih točk in nadaljuje izris dodatnih 100 točk navpično. Če tega ne želimo, moramo pred premikom »dvigniti« pero, podobno kot v primeru risalnikov. Pero dvignemo s pomočjo ukaza zelva.up(). Po premiku na želeno točko lahko naše pero vrnemo nazaj na ploskev s pomočjo ukaza zelva.down(). Risanje bomo nadaljevali z manjšim kvadratom (zelva.forward(50), zelva.right(90), zelva.forward(50), zelva.right(90), zelva.forward(50), zelva.right(90), zelva.forward(50).

Za potrebe novega risanja bomo zaslon izbrisali s pomočjo ukaza zelva.reset(). Naš večji kvadrat bomo narisali še enkrat s pomočjo zanke for, ki je videti takole:

for x in range(1, 5):
zelva.forward(100)
zelva.left(90)

Že z rahlo spremembo števila korakov in kota pa naš kvadrat spremenimo v osemkotnik:

for x in range(1, 9):
zelva.forward(50)
zelva.left(45)

Zanimive vzorce pa lahko dobimo tudi z večjim številom korakov in manj običajnimi koti:

for x in range (1,40):
zelva.forward(150)
zelva.left(105)
ali

for x in range(1, 25):
zelva.forward(size)
if x % 2 == 0:
zelva.left(160)
else:
zelva.left(230)

Zaslon lahko izbrišemo tudi z ukazom zelva.clear(). Bistvena razlika glede na ukaz reset je, da v tem primeru pobrišemo zaslon, želva pa ostane na svojem mestu in se ne vrača na sredino. Naši želvi lahko dodamo se želvaka z imenom tepko. Definiramo ga na enak način kot želvo, s pomočjo ukaza tepko=turtle.Pen(). Sprememba na zaslonu ni vidna takoj, saj sta obe puščici na istem mestu, zato tepka obrnemo za 180 stopinj tepko.left(180) in ga premaknemo za 100 točk. Na zaslonu imamo zdaj želvo in tepka, ki ju lahko premikamo neodvisno.

Da naše igranje (oziroma programiranje) z želvo popestrimo, lahko spremenimo tudi barvo. Podobno kot smo spuščali in dvigali pero, lahko tudi spreminjamo barvo naše želve oziroma peresa, s katerim rišemo. To storimo s pomočjo parametra color, pri katerem barvo definiramo s pomočjo razmerja osnovnih barv (rdeče, zelene in modre). Ker mešalnega razmerja posameznih barv verjetno večina ne obvlada, lahko uporabimo različne razpredelnice, s katerimi določimo potrebna razmerja. Eno od njih lahko najdemo na spletni strani http://www.december.com/html/spec/colorper.html. Za izris v zlati barvi na podlagi te razpredelnice vnesemo pred začetkom zanke vrstico zelva.color color(1,0.84,0) … saj smo iz razpredelnice dobili vrednosti rdeča = 100 %, zelena = 84 %, modra = 0 %.

Naš lik pa lahko tudi pobarvamo tako, da pred začetkom izrisa vnesemo ukaz zelva.begin_fill() in ga na koncu zaključimo/izvedemo z ukazom zelva.end_fill(). Za izris lika ter obrobo v črni barvi in obarvanje v rumeni je celoten »program« videti takole:

import turtle
zelva=turtle.Pen()
#definicija crne barve za izris
zelva.color(0,0,0)
zelva.begin_fill()
for x in range(1, 25):
zelva.forward(100)
if x % 2 == 0:
zelva.left(160)
else:
zelva.left(230)
#definicija rumene za polnjenje
zelva.color(1,0.84,0)
zelva.end_fill()

Končajmo tokratno besedilo s to lahkotno temo. V naslednjem besedilu pa bomo naše znanje uporabili za razumevanje praktičnih projektov na kartičnem računalniku Raspberry.

Se nadaljuje …

Slovarje uporabljamo podobno kot sezname.
S pomočjo modula lahko preprosto generiramo naključna števila.
Modul turtle omogoča preprosto risanje, podobno kot Logo.
Primer risanja z uporabo zanke
Turtle ponuja veliko možnosti.

NA KRATKO

Po ponovnem zagonu Pythonove lupine se podatki, ki jih ne shranimo, izgubijo. Eden od najpreprostejših načinov za shranjevanje je zapis v tekstovno datoteko. Pri pisanju pa moramo paziti na način zapisa – ali novi podatki pomenijo prepis obstoječih podatkov ali pa se jim dodajajo.

Uporaba obstoječih modulov nam prihrani delo in čas. S pomočjo modulov, ki jih dodamo, povečujemo funkcionalnost Pythona – tako lahko tudi manj vešč uporabnik hitreje doseže cilj, ne da bi poglobljeno poznal področje funkcionalnosti, ki jih določen modul obravnava.

Za mlajše je dobrodošla uporaba modula turtle – želve, ki jo poznamo tudi iz programskega jezika Logo. Modul omogoča risanje po zaslonu s pomočjo preprostih ukazov, kompleksnost slik pa je mogoče povečevati postopno z drugimi elementi Pythona (na primer različne zanke).

Moj mikro, Julij Avgust 2013 | Marko Koblar