PočetnaHelpdeskMala škola programiranja - Python programiranje

Mala škola programiranja – Python programiranje


Python programiranje – Python je programski jezik koji neki smatraju najboljim načinom za svladavanje osnova programiranja, ali i univerzalno rješenje za većinu problema koje je uopće moguće riješiti programski.

Python je prvenstveno objektno orijentirani programski jezik, za kojeg možda mnogi od vas nisu ni čuli. Pripada i tzv. skriptnim jezicima, jer ima po “defaultu” gomilu gotovih biblioteka koje sadrže veliku većinu objekata i funkcija koje mogu zatrebati, tako da se postiže izuzetno visoka razina programiranja. Ovo znači da često nećete biti u mogućnosti vidjeti što se odvija u pozadini (kao što je slučaj kod C-a), no to vam zapravo nije niti bitno. Ukoliko je brzina od velikog značaja te ukoliko se radi o većem i složenijem projektu čije je vrijeme izvršavanja relativno dugo (nekoliko minuta ili sati), Python će po performansama svakako podlijeći npr. C-u. Ali ipak, za izradu uobičajenih programa, Python je odličan odabir: lako je naučiti sintaksu, kôdovi su kratki i intuitivni te valja pohvaliti i izuzetno jednostavnu implementaciju grafičkog sučelja.

Postoje verzije Pythona 2.x i 3.x. Oni su međusobno nekompatibilni tako da morate odabrati verziju u kojoj ćete raditi. Preporuka je naravno 3.x iz jednostavnog razloga što je novija i moćnija. Ujedno, njome ćemo se baviti u ovom članku.

Za početak, potrebno je napraviti okruženje u kojemu ćete programirati. U prošlom članku “Kako početi programirati” je opisano što sve treba učiniti za početak rada u Pythonu. Ukoliko ste pročitali članak, možete preskočiti ovo poglavlje i krenuti s učenjem sintakse.
Dakle, “prirodno” okruženje u kojem se programira u Pythonu je svakako operativni sustav baziran na Linuxu (npr. široko prihvaćeni Ubuntu). Ukoliko imate takav operativni sustav, automatski imate interpreter koji je potreban za pokretanje programa pisanih u Pythonu. Teoretski, možete u text editoru napisati program i pokrenuti ga iz terminala upotrebom naredbe python [ime_datoteke.py], no ipak je bolja opcija instalirati IDE namijenjen za programiranje u Pythonu. Preporuka je instalirati IDLE, koji je IDE izdan od organizacije koja radi na usavršavanju Pythona. U Windowsu možete instalirati isto IDLE, postoji verzija i za Linux i za Windows.

Nakon što instalirate IDLE, možete početi s učenjem. Valja napomenuti da je Python zanimljiv zbog mogućnosti interaktivnog rada: možete programirati u “shellu” u kojem se nakon svake unesene linije kôda ista odmah i izvršava. U IDLE-u možete isprobati ovakav način rada u Shell Windowu.

python indeksiranje
Sintaksa Pythona je izuzetno jednostavna, što ćete vrlo brzo i vidjeti kroz primjere. Počnimo s prvim primjerom.

U prvom primjeru možete odmah uočiti jednostavnost sintakse. Ukoliko ste se susretali s programiranjem u drugim programskim jezicima, možete vidjeti da u Pythonu, za početak, nema oznake kraja retka (kao što je u C-u znak točka-zarez). Nadalje, nema deklaracije varijabli, varijabla se deklarira prvim pridruživanjem vrijednosti istoj. Primjer pokriva neke osnovne funkcije koje će svaki vaš program vjerojatno imati: upis podatka, pretvaranje tipa podatka, računanje i ispis.

Prva i druga linija kôda su poprilično slične pa ćemo pojasniti samo prvu. Naredba input() čita znakove s tipkovnice dok korisnik ne pritisne tipku Enter, a ispisuje na ekran poruku koja je navedena kao argument u zagradi. Ostatak programa se ne izvršava dok korisnik ne završi s unosom. Dakle, ukoliko imate barem malog doticaja s programiranjem, bit će vam intuitivno jasno da se u prvoj liniji izvršava prvo input(), pa zatim int() i naposlijetku pridruživanje varijabli broj1.

Nakon izvršavanja funkcije input(), uneseni tekst postaje argument funkciji int(). Važno je znati da Python svaki tekst unesen s tipkovnice prepoznaje kao string (tekst), dakle, nakon unosa broja on je i dalje tekst, ne broj. Funkcija int() upravo pretvara drugi tip podatka (u ovom slučaju string) u cijeli broj, te vraća taj broj. Naposlijetku, taj cijeli broj se pridružuje varijabli broj1. Kada bismo izostavili funkciju int(), unos bi se opet upisao u varijablu broj1, samo što bi to tada bio string te se ne bi mogle vršiti aritmetičke operacije nad njim na način na koji bismo to htjeli.

U trećoj liniji kôda imamo prvu matematičku operaciju u programu. Ona je intuitivno jasna: sadržaj varijable broj1 se množi sa sadržajem varijable broj2 te se rezultat sprema u varijablu rezultat.

U četvrtoj liniji kôda se lijepo vidi kako je jednostavno raditi sa stringovima u Pythonu (ovo će cijeniti oni koji su radili sa stringovima u C-u). String se definira stavljanjem u navodnike, kao što je to učinjeno ovdje. Na taj string se pomoću operatora “+” dodaje rezultat kojeg smo izračunali te ukupan string sprema u varijablu ispis. Rezultat matematičke operacije je cijeli broj, pa ga je potrebno pretvoriti u string funkcijom str(). Princip rada ove funkcije je sličan kao kod int(), samo što str() pretvara u string, a ne u cijeli broj.

Naposlijetku imamo ispis rezultata. Naredba print() ispisuje argument na ekran.
Zadnje dvije linije kôda je moguće sažeti u jednu, no ovako je pokriveno više funkcionalnosti u jednom primjeru. Umjesto četvrte i pete linije koda može se napisati jednostavno [print(“Umnozak brojeva je: “+str(rezultat))], bez uglatih zagrada. Doduše, cijeli program se može svesti na jednu liniju kôda, no tada je kôd nečitak i nerazumljiv: [print(“Umnozak brojeva je: “ + str(int(input(“Unesite prvi broj: “)) * int(input(“Unesite drugi broj:”))))].

Python programiranje – Neizmjenjivi objekti

U Pythonu postoje dvije osnovne kategorije podataka, a to su izmjenjivi i neizmjenjivi. I stringovi i brojevi pripadaju neizmjenjivim tipovima podataka. To za početak programiranja nije bitno, no kasnije kada budete pisali svoje funkcije, bitno je ipak znati što se događa s varijablama koje se prosljeđuju kao argumenti, kako biste mogli postići željeni cilj. Za početak, neizmjenjivi objekti (varijable) funkcioniraju tako da kada varijablu izmjenjujete, ne izmjenjuje se izvorna vrijednost nego se stvara nova, izmjenjena vrijednost, koja se sprema na novu memorijsku lokaciju. Ukoliko ne postoji više varijabla koja pohranjuje staru vrijednost, stara vrijednost se briše te se oslobađa taj dio memorije. Efekt neizmjenjivosti možete isprobati unošenjem sljedećih linija kôda: [a=5] [b=a] [a+=1] [a,b]. Prva linija varijabli a pridružuje broj 5. Druga linija varijabli b pridružuje isti memorijski prostor koji je pridružen varijabli a, pa dakle, b sadrži broj 5. Trećom linijom koda se vrijednost varijable a povećava za 1 (a+=1 je isto što i a=a+1), no u pozadini se vrijednost 5 kopira, uvećava za 1 (pa je sada 6) te sprema na novu memorijsku memorijsku lokaciju. Ta nova memorijska lokacija se pridružuje varijabli a. Četvrta linija samo ispisuje vrijednosti: “spominjanjem” varijable u liniji kôda ona se automatski ispisuje na ekran.

Ovaj cijeli koncept i pozadina rada može vam se učiniti nebitnim, no itekako je bitno ovo razumijeti, ako ne sada, ali zasigurno kada budete radili s listama. Kada bi recimo a i b bili izmjenjivi objekti, mijenjanjem varijable a bi se automatski izmjenila i varijabla b.
Postoje 4 tipa brojeva, a to su: cijeli broj (integer), dugi cijeli broj (long integer), broj s pomičnim zarezom (float) i kompleksni broj (complex). Kompleksne brojeve vjerojatno nećete koristiti pa im nećemo pridavati posebnu pažnju u tekstu (iako je dobro znati da možete na jednostavan način raditi s njima ukoliko se za tim ukaže potreba).
Na primjeru 2 ćemo upoznati neke osnovne operacije s brojevima te kako se međusobno ponašaju brojevi različitih tipova.

Dakle, prve dvije linije kôda su prilično jasne: varijablama br1 i br2 dodjeljujemo vrijednosti 7 i 2. Ovi brojevi su cijeli brojevi. U trećoj liniji kôda dijelimo ta dva broja. Očekivani rezultat je 3.5, što će se i i spisati na ekran. Primijetite da Python automatski pretvara cijele brojeve u brojeve s pomičnim zarezom te dobivamo valjani rezultat (u suprotnom, rezultat bi bio 3). Novodobivena vrijednost se ne sprema u nikakvu varijablu, jer nismo eksplicitno odredili da se vrijednost pohranjuje pa varijablama ostaju pridružene njihove početen vrijednosti.
U četvrtoj liniji koda imamo cjelobrojno dijeljenje pomoću operatora “//” pa je rezultat cijeli broj 3. Peta linija kôda od prvog broja oduzima drugi i rezultat sprema u prvi broj. Inače, praktički svaki izraz tipa [varijabla1 = varijabla1 (operator) varijabla2] je moguće skraćeno zapisati kao [varijabla1 (operator)= varijabla2], gdje varijabla2 može biti i konstanta (npr. [br1 -= 2] će od br1 oduzeti 2 i rezultat spremiti u br2).

Operator % u šestoj liniji predstavlja tzv. modulo, odnosno, rezultat ovakvog izraza je ostatak cjelobrojnog dijeljenja prve varijable s drugom. Iako se ne čini tako na prvi pogled, no modulo je često korišten u programiranju. Recimo, pomoću njega je lako odrediti je li broj paran: ako je rezultat 0, paran je, a ako je 1, neparan je. Dakako, to je samo jedna od mnogobrojnih primjena.

Naposljetku, u sedmoj liniji imamo potenciranje, koje je u Pythonu trivijalno jednostavno (npr. u C-u je potrebno koristiti funkciju za potenciranje): br1 se diže na potenciju br2 te se ispisuje rezultat.

Nekakve složenije matematičke operacije se izvode, kao i u svakom drugom programskom jeziku, jednostavnim slaganjem jednostavnijih izraza te grupiranjem istih pomoću zagrada.
Svi tipovi podataka u Pythonu su u principu objekti. Objekt je apstraktna tvorevina, koja je osnova objektno orijentiranog programiranja, a najbitnija karakteristika objekta je da podržava metode. Recimo, u Pythonu imamo klasu objekata koja predstavlja stringove. Toj cijeloj klasi pripada ujedno i određeni, ugrađeni skup metoda (funkcija) koje možemo primjenjivati nad svakim objektom unutar klase. Također, kaže se da je objekt instanca klase. To izravno znači sljedeće: u Pythonu imamo klasu stringova, koja podržava različite metode (npr. za zamjenu dijela stringa drugim stringom, traženje znaka unutar stringa itd.), a svaku od tih metoda možemo primjeniti na objektu (varijabli) koja je string.
Ovakav koncept rada sa stringovima je zapravo vrlo jednostavan i intuitivan za korištenje, što ćemo i vidjeti u primjeru 3. Vidjet ćete odmah i kako se jednostavno izrezuju dijelovi stringa.

python varijable
Prve dvije linije kôda su jasne. U trećoj liniji imamo prvo metodu, a to je replace(). Ova metoda kao argumente prima dva stringa. U stringu nad kojim se vrši ova metoda se traži prvi string (argument) te se zamjenjuje drugim stringom. Metoda vraća novodobiveni string, a string nad kojim je izvršena metoda ostaje neizmjenjen. Ovdje upravo vidimo neizmjenjivost stringova – svaka metoda koja se vrši nad stringom stvara izmjenjenu kopiju tog stringa, nikada ne izmjenjuje objekt nad kojim je metoda vršena.

Metoda find() u četvrtoj liniji vraća poziciju stringa (argumenta) unutar stringa nad kojim se vrši metoda. Pozicija prvog znaka unutar stringa je 0 (kao i u većini drugih programskih jezika) tako da, ako string ima deset znakova, pozicija prvog je 0, a zadnjeg je 9.
U zadnje dvije linije imamo izrezivanje stringova, koje se vrši operatorima “[“ i “]”. Ovim operatorima se u osnovi vrši indeksiranje, recimo “Pero”[1] će vratiti znak “e”, dakle znak unutar stringa koji je na poziciji 1. Preko indeksiranja i pomoću operatora “:” se postiže izrezivanje.

Između “[“ i “:” navodi se indeks početka izrezivanja, a između “:” i “]” kraj izrezivanja. Ukoliko nije naveden početak izrezivanja, ono se vrši od početka stringa, a ukoliko nije naveden kraj, ono se vrši do kraja stringa. Nadalje, indeks se može zamisliti kao granica između indeksiranog i prethodnog znaka, npr: “abcde”[2:4] postavlja granice “ab|cd|e” te vraća string “cd”. Negativni indeks postavlja granicu mjereći string otraga, pa je “abcde”[2:4] ekvivalentno “abcde”[2:-1] i “abcde”[-3:-1].

Sada bi vam trebala biti jasna tri izrezivanja koja imamo u zadnje dvije linije kôda. Novost je ubačeni string “ ” koji, kao i u ostalim programskim jezicima, zapravo predstavlja jedan znak,, koji pak predstavlja prelazak u novi red. Znak je ubačen kako bi se mogla vidjeti odvojenost dva stringa koji su rezultat izrezivanja stringa string2.
Primijetite da se metode i indeksiranja ne moraju nužno izvoditi na već definiranoj varijabli; potpuno su ispravni primjeri navedeni u tekstu kao što je “abcde”[2:4] ili čak “abcde”.replace(“a”, “A”).

Spomenut ćemo i funkciju (dakle, ne metodu, nego funkciju) koja kao argument prima string, a vraća duljinu istog. Funkcija je len(), koja će npr. u slučaju len(“abcde”) vratiti 5.
Nakon što smo prošli osnove rada s brojevima i stringovima prelazimo na izmjenjive tipove podataka te njihove najbitnije reprezentante: liste.

 

Python programiranje – Izmjenjivi objekti – Liste

Kao što smo već spomenuli, izmjenjivi objekti se izmjenjuju “na licu mjesta”, ne stvara se kopija. Ukoliko postoji više varijabli koje pokazuju na jedan izmjenjivi objekt, promjenom vrijednosti jedne varijable mijenja se i druga.

Lista je jedan takav objekt. Ona predstavlja sličnu strukturu kao što je niz (array) u C-u, no ipak je daleko moćnija, brža, a ujedno jednostavnija za korištenje. Prva bitna karakteristika liste u Pythonu je ta da podržava međusobno različite tipove podataka unutar sebe pa se tako npr. može u jednu listu staviti istovremeno broj, string i druga lista. Nadalje, ima širok spektar metoda koje možemo koristiti nad njom, podržava indeksiranje (izvedeno je na identičan način kao kod stringova), nadovezivanje jedne liste na drugu operatorom “+”. Kada se objekt dodaje u listu, pohranjuje se referenca na isti, a ne vrijednost. Sve navedeno imate priliku vidjeti (i isprobati) u primjeru 4, a kasnije ćemo obraditi i metode liste.

Za početak, lista se definira unutar uglatih zagrada, a elementi se odvajaju zarezom. U drugoj liniji kôda imamo prvu definiranu listu pod nazivom lista1, koja jednostavno sadrži tri broja: 1, 2 i 3. Nakon toga, definiramo drugu listu kojoj pridjeljujemo referencu na varijablu broj1, referencu na varijablu lista1 te string “Hello”. Bitno je razlikovati reference od vrijednosti, zasada (što vidimo u četvrtoj liniji pri ispisu) lista normalno sadrži očekivane vrijednosti: prvi element je lista [1,2,3], drugi element je broj 4 i treći element je string “Hello!”. No, ispis nakon pete i šeste linije kôda vam se može učiniti neočekivanim. U petoj liniji varijabli broj1 dodjeljujemo novu vrijednost 5, a lista (koja je izmjenjiva) ostaje nepromjenjena. Zašto? Zato jer smo varijabli broj1, koja je nepromjenjiva (cijeli broj) dodijelili novu vrijednost; varijabli je dodijeljena nova memorijska lokacija, a u listi je ostala stara referenca na staru vrijednost varijable broj1.

Pri izmjeni drugog elementa prve liste, u šestoj liniji, imamo drukčiju situaciju. Lista je izmjenjiv objekt pa se vrijednost drugog elementa mijenja “na licu mjesta”. Nakon ispisa u sedmoj liniji vidimo da se promijenio i prvi element druge liste, jer je to zapravo referenca na prvu listu koja je promjenjena. U ispisu se ispisuju obje liste jednostavno kako bi se vidjelo da promjena na jednoj listi utječe na promjenu druge liste.

Ispis osme linije kôda vam se može učiniti sličnim ispisu sedme linije. On je samo vizualno sličan, no ako bolje pogledate, ispisuje se zapravo zbroj dvije liste. Ta, rezultantna lista ima šest elemenata (što se vidi zadnjim ispisom) koji su redom: lista1, broj1, string “Hello!”, prvi element prve liste, drugi element prve liste i treći element prve liste.

U primjeru 5 imate priliku vidjeti najčešće metode te načine manipulacije koji se koriste pri radu s listama. Nakon inicijalizacije dvije liste (lista1 i lista2) imamo u trećoj liniji kôda implicitnu pretvorbu stringa u listu. U prvoj listi se prva dva elementa zamjenjuju trima novodobivenim elementima, koji su redom stringovi “b”, “l” i “a”. Ovdje vidimo da nije nužno mijenjati elemente istim brojem elemenata (na mjesto dva elementa su stavljena tri elementa). Nakon ovog pridruživanja, prepravljena lista se ispisuje.

U petoj liniji kôda vidimo novu metodu stringa: join(). Ona kao argument prima listu, a vraća string, koji se sastoji od elemenata liste odvojenih stringom nad kojim je metoda primjenjena. Ispisom u sljedećoj liniji kôda vidimo efekt ove metode – string poprima vrijednost “b-l-a”.

Nadalje, imamo metode append() i extend(). Ove dvije metode se razlikuju po tome što prva dodaje jedan element na kraj liste, a druga produžuje listu listom koja je navedena u argumentu metode. Prvoj listi je tako dodana lista [7,8,9] kao jedan element, pa prva lista sada ima 8 elemenata: [‘b’, ‘l’, ‘a’, 3, 4, 5, 6, [7,8,9]]. Nakon toga, izrezivanjem su joj prva tri elementa zamjenjena brojevima 1 i 2, te se rezultat vidi ispisom u jedanaestoj liniji kôda.
Druga lista je pomoću metode extend() produljena listom [9,8,7] te sada ima šest elemenata. Metoda reverse() “okreće” listu, a rezultat se vidi ispisom u dvanaestoj liniji kôda.
U zadnjoj liniji imamo metodu pop(), koja vadi zadnji element liste i vraća ga kao rezultat. Nakon izvršavanja ove metode, lista je kraća za jedan element, a upravo taj izvađeni element je rezultat izvršavanja te se ispisuje funkcijom print(). Ispisuje se i prepravljena lista, koja je dana kao drugi argument funkcije print().

U članku ste imali priliku upoznati Python na osnovnoj razini te spoznati osnovne principe ovog programskog jezika. Bitno je voditi računa o (ne)izmjenjivosti elemenata kako biste u svakom trenutku znali što se događa “na licu mjesta”, a što ne, te hoćete li promjenom jedne varijable utjecati na drugu. Primjetite također da sve navedene metode nad listama ne vraćaju listu kao rezultat, što je zapravo logično, jer, pošto su liste izmjenjivi objekti, metoda obavlja zadatak “na licu mjesta” pa nema potrebe za vraćanjem rezultata. Metode nad stringovima pak vraćaju rezultat, jer su stringovi neizmjenjivi pa svaka metoda čiji je cilj izmjena sadržaja stringa stvara novu izmjenjenu kopiju stringa te vraća nju. Novodobiveni string je potrebno spremiti u varijablu ili ispisati ako je to potrebno, jer u suprotnom rezultat metode se gubi.

 

Python programiranje – Promjene toka

Rječnici (engl. dictionary) su, uz liste, najfleksibilniji tip podataka u Pythonu. Oni su izmjenjivi objekti (kao i liste) pa ih je moguće izmjenjivati na licu mjesta, bez stvaranje kopije postojeće varijable. Visoko su optimizirani za nasumičan pristup (engl. random access) što znači da je brzina dohvata bilo kojeg elementa u rječniku jednaka. Ovo se može činiti nebitnim, no recimo, ako imate listu od 1000 elemenata i dohvaćate petstoti element, računalo mora “proći” kroz sve članove do petstotog kako bi dohvatilo traženi element. U slučaju rječnika, za dohvaćanje elementa potrebna je jedna operacija, nema iteriranja po svim članovima do traženog. Brzina rječnika osobito dolazi do izražaja ukoliko se radi s većom količinom podataka (npr. popis stanovnika ili telefonskih brojeva nekog mjesta i slično).

Nadalje, u slučaju liste smo imali dohvaćanje elementa pomoću indeksa. Ako želite dohvatiti element koji sadrži određenu vrijednost, morate ili znati na kojem se on točno mjestu nalazi u listi, ili pretražiti listu kako biste našli element. Kod rječnika je svaki element zapravo uređeni par ključ-vrijednost (engl. key-value) te je rječnik optimiziran za dohvaćanje vrijednosti pomoću ključa.

python škola primjer 5
Rječnik, kao i lista, omogućuje gniježđenje proizvoljne dubine i to bilo kojih vrsta elemenata. Na primjer, može prvi element biti broj, drugi element rječnik, a treći lista rječnika. Podržava i funkciju len(), pomoću koje saznajemo broj elemenata rječnika (naravno, broj elemenata prve razine ugniježđenosti).

U Primjeru 5 možete vidjeti osnove upotrebe rječnika. U prve dvije linije kôda napravljena su dva rječnika. Rječnik rjecnik1 je stvoren prazni rječnik, bez ijednog člana. Pomoću vitičastih zagrada je definiran upravo rječnik, kao što se pomoću uglatih zagrada stvara lista. U drugoj liniji je stvoren drugi rječnik kojem su inicijalno dodijeljene dva elementa: prvi element je string ‘nula’ s ključem ‘clan1’, a drugi je broj 1 s ključem ‘drugi clan’. Već ovdje vidimo kako je moguće spremati elemente različitih tipova unutar istog rječnika.

U trećoj liniji kôda je praznom prvom rječniku dodjeljen element ‘bla’ s ključem 5. Primijetite da je ovdje ključ broj, za razliku u drugoj liniji gdje je ključ bio string.
Nadalje, prvom rječniku dodajemo ključ 8 s elementom rječnikom, kojeg čine dva člana. Prvi član ima ključ tipa string, a drugi tipa broj. Ovo su dva različita ključa, iako su na prvi pogled jednaki, no prvi je string, riječ s dva ASCII znaka, ‘8’ i ‘1’, dok je drugi broj 81 koji podržava sve aritmetičke operacije s brojev ima. Bitno je zapamtiti da su dakle ‘81’ i 81 dvije apsolutno različite konstante, koje Python razlikuje (kao i većina drugih programskih jezika). Ovdje možete vidjeti da ne samo da elementi smiju biti različitog tipa, nego čak i ključevi, u ovom slučaju jedan je string, a drugi je broj.

Drugom rječniku, u petoj liniji kôda, mijenjamo element pod ključem ‘drugi clan’. Taj element se mijenja na licu mjesta te je rezultat ove operacije isti rječnik s istom memorijskom lokacijom kojem je izmjenjen drugi član u listu [2,3,4].

Pomoću zadnje dvije linije kôda se vrši ispis oba rječnika. Valja primijetiti da je poredak članova nepredvidiv, zbog upotrebe algoritama koji ubrzavaju dohvat elemenata. Tako je u prvom rječniku ispisan prvo član s ključem 8, a zatim s ključem 5. Jasno se vidi i to da rječnik ima točno onoliko elemenata koliko ih je stvoreno – u slučaju liste kad bismo imali članove s indeksom 5 i 8, ona bi morala imati najmanje 9 članova (od lista[0] do lista[8]), dok ovdje imamo točno dva člana. Zbog toga se rječnici u mnogim situacijama koriste umjesto listi kada se na prvi pogled niti ne čini da je to potrebno. Na primjer, zamislite da želite spremiti dva poštanska broja i pripadna mjesta, jedan je 10000 Zagreb, a drugi je 35000 Slavonski Brod i želite pomoću poštanskog broja dohvatiti naziv mjesta. Lista će imati 35001 član, od kojih je njih 34999 prazno, a dva su popunjena. Iako su elementi prazni, memorija za njih je rezervirana. U slučaju rječnika imamo točno dva člana: rjecnik[10000]=’Zagreb’ i rjecnik[35000]=’Slavonski Brod’. Dakle, za ta dva podatka imamo približno 17500 puta manje elemenata u rječniku nego u listi. Odnosno, drugim riječima, članova ima točno onoliko koliko je potrebno. Drugi, još reprezentativniji primjer bi bio pohranjivanje telefonskih brojeva. Zamislite koliki je tek gubitak memorije u tom slučaju.

 

Python programiranje – Metode rječnika

Kao i svi ostali objekti u Pythonu, tako i rječnik podržava veliku količinu vrlo korisnih metoda. Na Primjeru 6 ćemo proći kroz sve bitnije metode rječnika.

U prve dvije linije kôda inicijaliziramo dva rječnika. Slijede tri metode unutar funkcija print(): keys(), values() i items(). Metoda keys() vraća objekt tipa dict_keys, koji nalikuje klasičnoj listi, a sadrži listu ključeva unutar rječnika. Vraćena lista ključeva prvog rječnika se pomoću funkcije print() ispisuje na zaslon. U četvrtoj liniji je situacija slična, samo se umjesto liste ključeva vraća lista vrijednosti pa se ispisuju sve vrijednosti drugog rječnika. Naposlijetku, metoda items() vraća uređene parove ključ-vrijednost koji su dio rječnika nad kojim se metoda poziva.

Metoda update() u šestoj liniji kôda nadopunjuje rječnik nad kojim se vrši ista, rječnikom koji je naveden kao argument. Pri tome, ako oba rječnika posjeduju jednaki ključ, vrijednost drugog rječnika se upisuje pod taj ključ prvog rječnika. Rezultat se može vidjeti ispisom u sedmoj liniji kôda.

Metoda pop() izvlači element pod ključem koji je naveden u argumentu i vraća vrijednost, a taj element se iz rječnika briše. Ukoliko se navede nepostojeći ključ, javlja se greška i program prekida s radom.

U zadnje dvije linije imamo metodu get(), koja vrši istu funkciju kao pop() s razlikom što ne briše izvađeni element (rječnik ostaje nepromijenjen), a dohvatom nepostojećeg ključa se ne javlja greška, nego se vraća vrijednost None. Metodi get() se može proslijediti još jedan argument, koji predstavlja pretpostavljenu vrijednost koja će biti vraćena ako ključ nije nađen, pa ako u predzadnjoj liniji upotrijebimo rjecnik1.get(‘drugi’, ‘Nema elementa’), ispisat će se upravo string ‘Nema elementa’.

python škola primjer 6

 

Python programiranje – Petlje

U Pythonu imamo dvije vrste petlji: while i for. While petlja prima kao argument logički izraz, koji se ispituje ulaskom u petlju. Ako je izraz zadovoljen (istina, engl. true), izvršava se petljin blok naredbi. Nakon izvršavanja bloka naredbi, mijenja se tok programa te se opet izvršava linija u kojoj počinje petlja, odnosno, uvjet se opet ispituje. Blok naredbi se izvršava sve dok konačno uvjet više nije zadovoljen – petlja prestaje s radom, a program se nastavlja izvršavati nakon bloka naredbi pod petljom.

Ukoliko imate iskustvo u programiranju, koncept bloka naredbi vam je već zasigurno poznat. U principu, blok naredbi se može promatrati kao jedna logička cjelina, koja je na neki način odvojena od ostatka kôda, jer se nad tom cijelom cjelinom obavlja određena operacija. Tako na primjer petlja, dok je uvjet ispunjen, izvršava točno taj blok koji je naveden kao njen pripadajući blok, a kada uvjet više nije zadovoljen, blok se jednostavno preskače. U Primjeru 7 se vidi korištenje petlje while na osnovnoj razini.

Prve dvije linije kôda stvaraju varijablu i, koja će nam služiti kao brojač petlje i varijablu r0, koja predstavlja rječnik koji ćemo popunjavati.

Petlja while počinje u trećoj liniji kôda, a završava u petoj liniji. Petljina prva linija, odnosno, treća linija programa predstavlja “header” petlje. Navodi se ključna riječ while, iza koje slijedi u zagradi uvjet koji se ispituje te nakon toga dvotočka. Kada se ova linija izvršava, ispituje se uvjet u zagradi. Ukoliko je uvjet ispunjen, izvršavaju se sve linije kôda koje slijede i koje su uvučene. Te uvučene linije upravo predstavljaju spomenuti blok naredbi. Kada se izvrši zadnja uvučena linija, program nastavlja izvršavanje na “headeru”, odnosno, ispituje se uvjet. Ako je uvjet ispunjen, proces se ponavlja, a ako nije, izvršava se prva sljedeća neuvučena linija (u našem slučaju, šesta linija kôda).

Blok naredbi čine dvije uvučene linije. U prvoj liniji se rječniku pridružuje uređeni par koji čine ključ, varijabla i i vrijednost koja je isto ovisna o varijabli i. Varijabla i će imati vrijednost ovisnu o tome koliko puta se petlja odvrtila. Upravo u drugoj uvučenoj liniji se svakim prolaskom kroz petlju povećava vrijednost varijable i, pa će prvim prolaskom biti stvoren ključ 1, drugim prolaskom ključ 2 i tako sve do stvaranja ključa 26.

Desna strana jednakosti u prvoj uvučenoj liniji vam je vjerojatno zasad nepoznata. Ona stvara formatirani string (slično kao u C-u unutar funkcije printf()) tako što se navodi format s lijeve strane znaka “%”, a vrijednosti s desne strane. U ovom slučaju, s desne strane imamo izraz (i+64), koji prvim prolaskom kroz petlju ima vrijednost 65. On se, pomoću lijevo navedenog formata ‘%c’, pretvara u znak “A”, jer je ASCII vrijednost znaka “A” upravo 65. Kada varijabla i bude imala vrijednost 2, znak će biti “B” i tako sve do zadnje vrijednosti 26, kada će se pridružiti znak “Z”.

python škola primjer 7
Nakon petlje se ispisuje, pomoću funkcije print(), sadržaj generiranog rječnika. Vidimo da smo na vrlo jednostavan način dobili rječnik pomoću kojeg možemo npr. postaviti upit koje je osamnaesto slovo engleskog alfabeta i dobiti odgovor, što je i učinjeno zadnjom linijom kôda.

Općenito kod petlji treba paziti na nekoliko stvari. Prvo, pri uvlačenju linija, sve uvlake moraju biti jednake – nije bitno kako su postignute (tab ili razmaci), no za jedan blok moraju biti iste. Doduše, ukoliko koristite IDLE ili neki drugi IDE, vjerojatno ćete automatski dobiti potrebne uvlake svakim pritiskom na tipku Enter. Nadalje, jako je bitno kod petlji paziti na zadovoljenost uvjeta koji se ispituje. Ukoliko ne postavite dobar uvjet, može se dogoditi da program ostane u beskonačnoj petlji. Npr, ako biste uklonili liniju [i+=1] ili biste stavili uvjet (i>0), upravo bi se to dogodilo, jer bi uvjet ostanka u petlji stalno bio ispunjen. Preporučamo da to ne pokušavate.

Druga petlja koja će biti objašnjena je for. Ona se bitno razlikuje od petlje for u C-u ili C++-u, jer predstavlja zapravo tip petlje koji je u nekim programskim jezicima poznat kao foreach. U Pythonu, for se koristi za iteriranje po sekvencijskom tipu podatka (npr. listi, vrijednostima rječnika i slično). Primjer 8 nam zorno pokazuje kako petlja for funkcionira te kako još jednostavnije provesti generiranje rječnika iz Primjera 7.

python škola primjer 8
U drugoj liniji Primjera 8 počinje petlja for, odnosno, to je njen header. Navodi se ključna riječ for, nakon nje varijabla koja predstavlja brojač, zatim ključna riječ in i naposlijetku sekvencijska struktura kroz koju se iterira (npr. lista). Novoupotrebljena funkcija range() generira listu čiji se elementi formiraju prema argumentima. Ukoliko se zada jedan argument, nekakav broj N, generira se lista od N članova čiji je prvi član 0, drugi 1 i tako uzlazno sve do N-1. Ako se zadaju dva argumenta, prvi predstavlja prvi član niza, a drugi argument predstavlja do kojeg broja se generira niz, bez tog broja. U našem slučaju, generira se lista [1,2,3,…,25,26]. Može se zadati i treći argument, koji predstavlja korak, pa kad bismo dodali broj 3 u našem slučaju, generirala bi se lista [1,4,7,…,22,25].

Petlja for pridružuje varijabli navedenoj između ključnih riječi for i in (u našem slučaju varijabla i) svaku vrijednost iz strukture navedene iza ključne riječi in. Tako će prvim prolaskom kroz petlju biti i=1, drugim i=2 i tako redom sve do i=26. Vidimo da ovaj program obavlja istu radnju kao i Primjer 7, samo je još sažetiji i jednostavniji.
Postoje još tri naredbe koje se koriste kod petlji, ali će o njima biti riječi kasnije iz praktičnih razloga. Prelazimo na naredbu grananja: if.

 

Python programiranje – Naredba grananja

U Primjeru 9 razdvajamo u dva rječnika brojeve od 1 do 19 na one koji su djeljivi s 3 i na one koji nisu djeljivi s 3. Ključeve rječnika će činiti upravo ti brojevi, a vrijednosti će biti rezultati dijeljenja tih brojeva s 3.

Petlja for će pridruživati varijabli i sve vrijesnosti od 1 do 19. U četvrtoj liniji koda imamo naredbu if koja se koristi za grananje u programu. Ukoliko je uvjet unutar zagrade naredbe if zadovoljen, izvršava se njezin blok naredbi. Ako uvjet nije zadovoljen, izvršava se blok naredbi pod naredbom else (u ovom slučaju ispis da je broj djeljiv s 3). Uvjet, općenito bilo koje naredbe je ispunjen ako izraz unutar zagrada te naredbe generira istinu (true), koju predstavljaju i svi brojevi osim nule. Tako se ovdje provjerava ostatak djeljenja varijable i s brojem 3. Ako ostatka ima, odnosno, ako on nije jednak nuli, broj nije djeljiv s 3 i izvršava se blok naredbi kojeg čine peta i šesta linija kôda. U petoj liniji se u rječnik r1 sprema taj broj kao ključ te rezultat djeljenja tog broja s 3 kao vrijednost.
Šesta linija nam donosi jednu od tri naredbe za petlje koje nismo spomenuli, a to je u ovom slučaju continue. Ova naredba mijenja tok programa tako što se vraća na header petlje unutar koje se nalazi, ne izvršavajući ostatak bloka naredbi te petlje. Tako se ovdje preskače sedma, osma i deveta linija, jedan prolazak kroz petlju je završio te se ponovno izvodi header petlje for. U sedmoj liniji imamo naredbu else, čiji se blok izvršava u slučaju da uvjet prethodne if naredbe jednake razine ugniježđenosti nije zadovoljen. To je u primjeru samo osma linija kôda – ispis da je broj djeljiv s 3.

Na kraju petlje je još pridruživanje djeljivog broja rječniku r0 te rezultata dijeljenja tog broja s 3. Ovdje je upotrebljen operator “//”, koji predstavlja cjelobrojno djeljenje, jer je broj svakako djeljiv s 3 pa nema potrebe pamtiti rezultat kao broj s pomičnim zarezom. Valja primjetiti da upravo zbog upotrebljene naredbe continue, ova se linija neće izvršiti ako broj nije djeljiv s 3.

Program je mogao biti kraće i jednostavnije napisan, no ovako je obuhvaćeno više naredbi odjednom. Primijetite da bismo mogli izbaciti naredbu else i osmu liniju koda staviti na razinu naredbe if (pomaknuti za jednu uvlaku ulijevo) ili izbaciti naredbu continue, a devetu liniju koda dodatno uvući (staviti u blok naredbi naredbe else).
U Primjeru 10 se koriste dvije nespomenute naredbe za petlje: break i else. Ovaj program provjerava je li uneseni brojevni niz rastući s tim da uzastopne znamenke smiju biti jednake, samo znamenka ne smije biti manja od prethodne.

Na početku je funkcija za upis podatka a zatim inicijalizacija varijable j koja će pamtiti prethodni broj. Inicijalno je varijabla postavljena na nulu. Definirana je kao string, jer podaci uneseni sa tipkovnice su tipa string pa se ovako izbjegava nepotrebno pretvaranje stringa u broj. Kao što je npr. broj 3 veći od broja 1, tako je i string ‘3’ veći od stringa ‘1’ (ima veću ASCII vrijednost.)
Imamo tri linije kôda unutar petlje od kojih prva provjerava je li trenutna znamenka manja od prethodne. Ukoliko je, ispisuje se da niz nije rastući i izvršava se naredba break. Naredba break jednostavno zaustavlja rad petlje u kojoj se nalazi, a program nastavlja s izvođenjem iza bloka petlje. Nakon naredbe break je pridruživanje trenutne znamenke varijabli j, koja će onda služiti za usporedbu pri sljedećem prolasku petlje i koje će se obaviti samo u slučaju da znamenka nije manja od prethodne.

python škola primjer 9
Naredba else u osmoj liniji kôda je vezana za petlju for. Njezin blok se izvodi u slučaju da je petlja normalno završila s radom, dakle, bez naredbe break. Tako u ovom primjeru, čim se prvi puta naiđe na znamenku koja je manja od prethodnika, naredba break prekida izvođenje petlje i naredba else (odnosno njezin blok) se ne izvodi. Ukoliko niti jedna znamenka nije manja od prethodnika, petlja će normalno završiti s radom te će se izvršiti i blok naredbe else, kojeg u ovom slučaju čini ispis poruke da je niz rastući.

Python programiranje – Funkcije
Znanjem stečenim do sada ne možete definirati vlastite funkcije kojima ćete skratiti i olakšati programiranje. Za to su vam potrebne osnove rada s ručno definiranim funkcijama u Pythonu: moći ćete nekakav zadatak koji se ponavlja unutar istoga programa izvršavati pomoću vlastite funkcije, koju možete pozvati u bilo kojem dijelu programa te taj zadatak odraditi bez potrebe za ponovnim unosom većeg dijela kôda. Obradit ćemo i osnove rada s datotekama pa ćete nakon pažljivog čitanja zaista moći napraviti program koji može za vas imati veliku uporabnu vrijednost. Općenito, u svim primjerima, naglasak će biti na radu sa stringovima, jer Python podržava čitav niz vrlo korisnih funkcija za rad sa stringovima.
Pošto je Python objektno orijentiran programski jezik, string je jedna od mnogih klasa objekata u Pythonu te je upravo zbog toga rad sa stringovima brz i intuitivan.

python škola primjer 10

Naposljetku, većina postojećih programa obavljaju zadatke nad stringovima jer svaki dokument koji se obrađuje ili tekst kojeg korisnik unosi, a koji treba biti obrađen, podrazumijeva upravo rad sa stringovima. Za početak, primjer 11 je program koji od korisnika traži unos rečenice u terminal. Program čita unose, dakle, više rečenica, sve dok korisnik ne unese riječ “exit”. Za svaki unos, program odmah ispisuje istu rečenicu s time da postavlja veliko slovo na početku rečenice te na kraju stavlja točku ukoliko je nema.

python škola primjer 11

Primjer 11 možete vidjeti u okviru sa strane kao i screenshot pri izvršavanju. Napominjemo, ovaj primjer ne sadrži ručno definiranu funkciju. U prvoj se liniji nalazi prvi unos korisnika koji će se obrađivati. Ostatak programa odnosno svi zadaci koje program obavlja smješteni su u while petlju kojoj je uvjet ostanka da je uneseni tekst različit od “exit”. Prvo se u petlji nad unesenim tekstom vrši metoda capitalize (). Ta metoda vraća kopiju stringa nad kojim se vrši metoda, a prvo slovo se pretvara u veliko ukoliko je bilo malo. Kao što se vidi i u primjeru izvođenja, metoda je vrlo moćna jer podržava i nestandardne znakove. Metoda ispravlja znakove u cijeloj rečenici na način da sva slova pretvara u mala slova, a prvo slovo rečenice pretvara u veliko slovo. U drugoj liniji unutar petlje provjerava se kraj unesenog teksta te ukoliko je on različit od točke, upitnika i uskličnika, na tekst se dodaje točka. Nakon toga se ispisuje ispravljena rečenica, ponovno se učitava tekst te se program nastavlja izvoditi na ispitivanju uvjeta ostanka u petlji.

Možda se pitate čemu taj primjer i kakve on ima veze s funkcijama? Naime, u takvom programu zaista nema potrebe za upotrebom ručno definirane funkcije. Petlja while osigurava mogućnost višestrukog obavljanja iste zadaće odnosno izvodi jednake obrade za više linija teksta. U našem slučaju, uvjet je jednostavan, a obrada se izvodi pri svakom unosu novog teksta. Međutim, što ako određene unose želimo preskočiti ili se npr. ukaže potreba da jednom petljom izvrtimo nekoliko unosa, zatim izvršimo nekakvu obradu, a nakon toga opet pomoću petlje ispravljamo rečenice? Tada bismo morali sadržaj cijele petlje kopirati i zalijepiti ga u tu novu petlju te bi tada dio kôda bio bespotrebno više puta upisan u istom programu.

Primjer 12 obavlja identičnu zadaću kao i primjer 11, razlika je samo u načinu pisanja programa – definiramo vlastitu funkciju imena ispraviRecenicu koja ima jedan argument “arg”. Od prve do pete linije kôda je definicija nove funkcije. Definicija započinje ključnom riječi “def” iza koje se navodi ime funkcije te se u zagrade navode argumenti koje funkcija podržava. U našem slučaju to je jedan jedini argument koji je nazvan “arg”. Cjelina koja se nalazi unutar definicije funkcije, a ne pripada headeru koji počinje ključnom riječi “def”, često se naziva tijelom funkcije. Vidimo da se ovaj primjer sadržajem ne razlikuje puno od glavne obrade iz primjera 11; razlika je samo u upotrebi nove varijable arg i liniji [return(arg)]. Vratit ćemo se još na samu definiciju funkcije. U glavnom dijelu programa vidimo da je cijela obrada, koja je prebačena u definiciju funkcije, svedena na poziv funkcije linijom [tekst = ispraviRecenicu(tekst)].

python škola primjer 12
Što se zapravo događa ovim pozivom? Kopija varijable tekst se kao argument predaje funkciji naziva ispraviRecenicu. Izvođenje programa se nastavlja na prvoj liniji kôda, gdje se sadržaj varijable “tekst” kopira u varijablu “arg” te se u funkciji obrađuje varijabla arg. Na kraju funkcije, naredbom povratak vraća se vrijednost varijable arg. Program nastavlja izvođenje gdje je stao prije poziva funkcije te se varijabli “tekst” dodjeljuje sadržaj varijable “arg” nakon obrade unutar funkcije. Dakako, sadržaj se svih varijabli unutar definicije funkcije gubi prilikom završetka izvođenja funkcije. Završetak izvođenja funkcije se određuje naredbom povratak odnosno ukoliko funkcija ne vraća nikakav rezultat, krajem tijela funkcije (poništenjem uvlake). Općenito kod funkcija postoji nekoliko bitnih stvari za zapamtiti. Prvo, sve definicije funkcija moraju se pisati prije prvog poziva funkcije. To je nužno zbog načina rada većine programskih jezika pa tako i Pythona. Naredbom defdefinira se funkcija pa ukoliko ona nije prvo definirana, a zatim pozvana, Python neće prepoznati što predstavlja tekst “ispraviRecenicu(tekst)”. Kada je funkcija definirana, njen naziv je pohranjen u posebnu tablicu (koja je korisniku pa i programeru nevidljiva) kako bi eventualnim kasnijim pozivom Python “znao” da je to funkcija i gdje se nalazi njena definicija.

Nadalje, definicija funkcije se ne izvršava automatski nego tek pozivom. To znači sljedeće: program se neće izvršavati od prve linije kôda (u našem primjeru) nego tek od sedme linije. Definicije svih funkcija se preskaču, a linije unutar definicije se izvršavaju jedino pozivom funkcije. Primjer 12 ćemo nadograđivati kroz članak, no sada ćemo napraviti blagu digresiju kako bismo objasnili važnost poznavanja rada Pythona pri definiranju vlastitih funkcija. Argumenti i povratna vrijednost funkcije Primjer 13 donosi rad s listama pomoću ručno definirane funkcije. U screenshotu možete vidjeti što program radi: za upisana dva cijela broja ispisuje jesu li poredani odnosno je li prvi manji od drugoga. Program ih sprema u listu pri samom upisu, a ako nisu poredani, premješta ih tako da prvi član bude manji od drugoga. Prvo ćemo objasniti glavni dio programa. U varijablu naziva “brojevi” odmah se upisuju prvi i drugi broj te je varijabla tipa liste. Članovi, pošto su stringovi jer su uneseni s tipkovnice, funkcijom int() se pretvaraju u cjelobrojne vrijednosti te nakon toga spremaju u listu.

python škola primjer 13
U drugoj liniji kôda imamo obavljenu veliku većinu zadaće glavnog programa. U uvjetu naredbe if ispituje se vraća li funkcija poredaj() istinu, za listu koja joj je predana kao argument. Kao što već vjerojatno znate, istina se može predočiti i kao broj 1, a laž kao broj 0. Definiciju funkcije poredaj() objasnit ćemo uskoro – zasad je dovoljno znati da ona provjerava listu i izmjenjuje je ukoliko je prvi broj veći od drugoga. Ako je prvi broj manji od drugoga, funkcija vraća 1, a ako nije, funkcija vraća 0 i zamjenjuje članove liste. Naposljetku, ako je funkcija vratila 1, ispisuje se poruka da su brojevi bili poredani. U suprotnom, ispisuje se da nisu bili poredani. Primjećujete da ako blok naredbe if (kao i for, while, else itd.) sadrži samo jednu naredbu, možemo je pisati u nastavku headera, bez potrebe za prelaskom u novi red i uvlačenjem retka. Na kraju glavnog programa ispisuje se lista “brojevi”. Primjećujete da su brojevi koje je korisnik unio spremljeni upravo u tu istu varijablu te nema pridruživanja novih vrijednosti toj varijabli u obliku [varijabla = funkcija(varijabla)], kao što je to bio slučaj u prošlom primjeru. No, ipak, funkcija je izmijenila listu. Zašto? Sada upravo dolazimo odnosno vraćamo se na temu iz prvog članka o Pythonu, a to je pitanje izmjenjivih i neizmjenjivih objekata.

python tijek programa
Lista je izmjenjiv objekt dok je string neizmjenjiv. Zato, ako u funkciju prosljeđujemo string, funkciji se predaje kopija stringa odnosno moglo bi se reći sadržaj varijable. Obavlja se tzv. prenošenje vrijednosti parametra te bilo kakva izmjena te vrijednosti unutar funkcije ne mijenja sadržaj originalne varijable čija je vrijednost predana funkciji. Zato se pri obradi stringa uvijek varijabli eksplicitno mora pridružiti vrijednost koju funkcija vraća. U suprotnom bi se izmijenjena vrijednost trajno izgubila jer sve varijable unutar funkcije “žive” samo od poziva do završetka iste. U slučaju liste, situacija je drukčija. Pošto je lista izmjenjiv objekt, pri predaji liste funkciji prenosi se njena adresa.

Takvo ponašanje bi se moglo usporediti s predavanjem pointera funkciji u programskom jeziku C te se takvo prenošenje naziva još i prenošenje reference parametra. Prenošenjem adrese, funkcija ima pristup upravo varijabli koja joj je prenesena te svaka izmjena takve promjenjive varijable unutar funkcije, rezultira izmjenom originalne varijable koja je predana funkciji. Zbog toga, nije potrebno eksplicitno obaviti pridruživanje povratne vrijednosti funkcije varijabli te se varijabla može izmijeniti jednostavnim pozivom oblika [funkcija(varijabla)]. To je i učinjeno u primjeru 13.

Mi smo u našem slučaju iskoristili sposobnost funkcije da vrati rezultat u drugu svrhu: ispitivanje nekog svojstva varijable koja je proslijeđena u nju. Lista se “na licu mjesta” posloži ako je potrebno, a funkcijom vraćamo istinitost tvrdnje da je lista bila poredana. Program bi jednako dobro funkcionirao, iako bez ispisa je li lista bila poredana, kada bismo umjesto druge linije glavnog dijela programa upisali liniju [poredaj(brojevi)] te uklonili sljedeću liniju kôda (ne smijemo ostaviti else ako nema naredbe if prije nje). Definicija funkcije nije komplicirana, ali valja obratiti pažnju na par stvari. Prvo, ulaskom u blok naredbe if obavlja se zamjena prvoga i drugoga člana liste te funkcija vraća vrijednost 0. Pri izvršavanju funkcije return(), funkcija prestaje s radom pa zadnja linija u tijelu funkcije [return(1)] neće biti niti izvršena.

Zbog toga nije potrebna naredba else i pripadajući blok u koji bismo stavili samo liniju [return(1)]. Preporučujemo da dobro zapamtite princip zamjene vrijednosti dvaju varijabli, ukoliko se niste prije susretali s njim. To je klasičan pristup koji se praktički uvijek koristi kada je potrebno zamijeniti sadržaje dvije varijable. Potrebna je dodatna varijabla koja je ovdje imenovana “temp”, što je opet prilično raširen naziv za varijablu koju smatramo privremenom (temp je skraćenica od engleske riječi “temporary” čiji je prijevod “privremen”). U privremenu varijablu spremamo vrijednost prve varijable (prvoga člana liste), nakon toga u prvu varijablu spremamo vrijednost druge varijable te naposljetku vrijednost privremene varijable (koja je jednaka prvotnoj vrijednosti prve varijable) spremamo u drugu varijablu. Time je prvoj varijabli pridružena vrijednost druge, a drugoj vrijednost prve. Ovdje smo argument nazvali listaArg.

Dobro je imati u navici da se argumentima dodaje nekakav prefiks ili sufiks koji se dodaje nazivima svih argumenata jer u većim programima lako se izgubiti u gomili varijabli koje predstavljaju argumente i onih koje su izvan funkcije. Dodavanjem npr. sufiksa “Arg”, kao što smo mi učinili, bez dvojbe znate da je riječ o argumentu funkcije u kojoj se varijabla nalazi.

Proširenje primjera 12
U primjeru 14 poboljšana je funkcionalnost primjera 12. Poboljšanja su sljedeća: samo prvoj riječi u rečenici se postavlja veliko početno slovo te se izbacuju uskličnici, upitnici i točke na krajevima riječi unutar unesenog teksta (na kraju se zadržavaju, kao i u primjeru 12). Za početak, imamo dvije ručno definirane funkcije. Druga poboljšano ispravlja rečenicu dok prva služi za provjeru završava li riječ koja je predana kao argument točkom, uskličnikom ili upitnikom te poduzima potrebne radnje nad tom riječi. Redoslijed definiranja te dvije funkcije je bitan jer funkcija ispraviRecenicu() koristi funkciju oznakaKraja().

Funkcija oznakaKraja() prima dva argumenta: prvi je argument riječ koju se provjerava dok drugi argument govori je li potrebno na učitanu riječ dodati točku ili ne. Vidimo da je u headeru te funkcije upisano “dodajArg = 1”. Ta jednakost govori da je pretpostavljena vrijednost toga argumenta 1 što znači da funkciju smijemo pozvati bez predaje drugoga [tab]argumenta te će u tom slučaju on poprimiti vrijednost 1. Ako se pri pozivu funkcije ipak proslijede dva argumenta, varijabla dodajArg će sadržavati drugi predani argument. Obično se u praksi programiranja funkcija modelira tako da je ili pretpostavljena vrijednost logična (npr. definicija neke funkcije zadana pomoću [def obradi(arg1, ispisiGresku=1)] koja je modelirana da po običaju ispisuje pogrešku) ili se pretpostavljena vrijednost puno češće koristi od svih ostalih (npr. [def potenciraj(broj, potencija=2)] jer će se broj najčešće kvadrirati). U našem slučaju, pretpostavljena vrijednost je 0 jer nulom, u našem programu, podrazumijevamo potrebu za uklanjanjem jednoga od triju spomenutih znakova na kraju riječi unutar rečenice dok jedinicom podrazumijevamo dodavanje znaka na kraj rečenice ukoliko je potrebno.

python škola primjer 14
Naravno, više će se provjera obaviti unutar rečenica jer je više riječi unutar rečenica nego na kraju njih. Iako za naš primjer nije izbor pretpostavljene vrijednosti argumenta od velikog značaja, jer nam promjena vrijednosti ne skraćuje vrijeme pisanje programa. Ipak, dobro je imati na umu ideju zašto uopće pretpostavljati vrijednost nekog argumenta. U funkciji oznakaKraja(), ukoliko riječ ne završava niti jednim od kritičnih znakova, provjerava se je li varijabla dodajArg istinita. Ako jest, riječi se dodaje točka, preskače se else blok i vraća se izmijenjena riječ. Primjećujete da su ta dva uvjeta mogla biti stavljena u jednu if naredbu, no zbog bolje preglednosti, to nije učinjeno. Blok ­naredbe predstavlja slučaj kada riječ završava nekim od kritičnih znakova. Ako završava i ako je varijabla dodajArg jednaka nuli, riječ se skraćuje za jedan znak. Uzima se dio riječi od prvoga do predzadnjega znaka te se taj dio kopira u istu varijablu. Nakon svega slijedi vraćanje obrađene riječi pomoću funkcije return().

U funkciji ispraviRecenicu() na početku string pretvaramo u listu, a kao odjeljivač koji određuje gdje će string presijecati, pretpostavljen je razmak. U varijablu tempList pohranjuje se takva novo dobivena lista. Pomoću for petlje iterira se po svim članovima liste i to tako da se varijabli “i” dodjeljuju svi brojevi od 0 do broja članova liste. Na prvi pogled se čini da se moglo iterirati i pomoću [for rijec in temList], no to bi zakompliciralo kôd i produljilo vrijeme njegovoga upisivanja. Naime, tada bismo teško mogli odrediti koja je točno riječ zadnja riječ u unosu. Morali bismo na neki način prebrojavati koliko je članova liste obrađeno i kolika je duljina liste.

Tako je to prebrojavanje izvedeno u samom headeru petlje bez potrebe za eksplicitnom definicijom brojača, inicijalizacijom i inkrementiranjem istoga. Uvjet prve if naredbe je da riječ koja se obrađuje nije zadnja u tekstu. Riječ nije zadnja upravo ako je njen indeks manji od duljine liste umanjene za jedan. Naime, ako unos sadrži pet riječi, zadnja riječ ima indeks 4, a sve prethodne riječi imaju indeks manji od 4. U tom slučaju, riječ se obrađuje pomoću opisane funkcije oznakaKraja() pri čemu je implicitno određeno izostavljanjem drugog argumenta da se treba maknuti kritični znak s kraja riječi ukoliko on postoji. Naredba else podrazumijeva sve ostale slučajeve odnosno jedan preostali slučaj, a to je da je riječ obrade zadnja u unesenom tekstu. Tada funkciji oznakaKraja() prosljeđujemo i argument 1 pomoću kojeg će funkcija “znati” da je potrebno dodati točku na kraj riječi, ukoliko riječ ne završava kritičnim znakom. Time je postignuta selektivna manipulacija kritičnim znakovima: uklanjaju se tamo gdje nisu potrebni, a stavljaju tamo gdje jesu. Dvije su vrlo slične funkcije objedinjene u jednu koja pokriva velik dio zajedničkoga posla. Napravljena je velika ušteda vremena i prostora pri kodiranju, a i program je fleksibilniji za izmjene.

Funkciji je lako dodati mnoštvo drugih opcija ukoliko se ukaže potreba za njima. Sve je navedeno glavni cilj upotrebe funkcija koji je u ovome slučaju postignut. Već prije upotrijebljena metoda capitalize() sad se lako primjenjuje samo na prvoj riječi unesenog teksta jer je tekst “razbijen” u listu. Jednostavno metodu primijenimo samo na prvu riječ odnosno na nulti član liste. I na kraju funkcije ispraviRecenicu vraća se obrađeni tekst tako što se lista pomoću metode join() pretvara natrag u string te vraća funkcijom return() u glavni program. Glavni je dio programa jednak onome u primjeru 12. Sva važnija znanja potrebna za definiranje vlastite funkcije su iznesena. Bitno je poznavati sustav predaje argumenata funkciji jer nekad ćete definirati funkciju za listu, a nekad za string – kod liste se varijabla izmjenjuje u funkciji dok se kod stringa izmjenjuje samo kopija varijable, odnosno njezin kopirani sadržaj.

Postoje programi kod kojih ne treba forsirati upotrebu funkcija. Ukoliko se jedan zadatak ne obavlja više puta unutar programa ili se ponavlja, ali se ponavlja zbog petlje, nema potrebe za upotrebom funkcije. Tako npr. u primjeru 12 je samo demonstrirana upotreba funkcije kako bi se vidjele razlike u kôdu između primjera 11 i 12. Realno, rješavanje zadanoga problema kôdom iz primjera 12 je gubljenje vremena jer je kôd duži. Ipak, ukoliko se radi o programu koji će biti potrebno proširivati (kao što je ovdje slučaj) isplati se uložiti nešto više vremena pa zadatak koji se proširuje preoblikovati u funkciju. Sve ovisi o situaciji u kojoj se programer nalazi te kakvi su mu planovi za budućnost glede programa na kojem radi.

Python programiranje – Objekti
Do sada smo spominjali objekte samo u kontekstu postojećih objekata. Tako imamo klase int, string, dict i mnoge druge, a kojima pripadaju sve varijable koje stvorimo, odnosno, instanciramo iz određene klase objekata. U pozadini izvršavanja kôda, upisom linije [x=’tekst’], instanciramo objekt klase string i naziva x. Nad ovim objektom možemo primjeniti i neku od brojnih metoda koje su pridružene klasi objekata string, recimo, upisom linije [y = x.capitalize()] metoda će stringu x postaviti veliko početno slovo, ostala će pretvoriti u mala te će se rezultat spremiti u varijablu y.

Programiranjem većih i složenijih programa javlja se potreba za stvaranjem vlastitih klasa, koje će biti točno prilagođene našim potrebama. Čak i pri izradi manjih programa, objektno orijentirani pristup može biti od velike pomoći, ukoliko je priroda problema pogodna za takav pristup. U principu, korištenje objekata pojednostavljuje programiranje svugdje gdje se javlja potreba za stvaranjem mnoštva nekakvih struktura, koje imaju jednake opće karakteristike. Klasi se također mogu dodati i metode, koje pak predstavljaju izvršavanje funkcije nad nekim objektom koji pripada toj klasi. Svaki objekt može imati i svoje privatne varijable (atribute) koje će opet s jedne strane biti zajedničke za cijelu klasu, a s druge strane imati sadržaj strogo vezan za vlastiti objekt.

Tipičan primjer za upotrebu objekata je nekakva baza podataka. Recimo, želimo napraviti program pomoću kojeg ćemo pohranjivati proizvode te njihove karakteristike. U tom slučaju, mogli bismo napraviti klasu “proizvod”, koja će podržavati atribute “naziv” i “cijena” te metodu “promijeni cijenu”. Ako imamo 100 proizvoda, svaki ćemo predstaviti jednim objektom klase “proizvod”, svakom ćemo dodijeliti naziv i cijenu, te ćemo biti u mogućnosti pomoću metode “promijeni cijenu” mijenjati svakom proizvodu atribut “cijena”.
Kroz članak ćemo napraviti program koji će biti nekakav oblik kviza. Osnova kviza će biti klasa imena “zadatak”, a za svaki zadatak ćemo za početak vezati pitanje i odgovor, što ćemo kroz primjere unaprijediti do prave male računalne igrice.

Za početak, u primjeru 15 možete vidjeti kako se definira najosnovniji oblik klase te instanciraju objekti i mijenjaju njihovi atributi. Ključnom riječi “class” i navođenjem imena počinje definicija klase objekata, iza čega slijedi dvotočka, odnosno, tipični početak bloka u Pythonu. Prvom linijom kôda dakle definiramo novu klasu objekata, naziva “zadatak”. Druga i treća linija čine tijelo definicije klase. U njima imamo varijable naziva “pitanje” i “odgovor” te su im pridružene inicijalne vrijednosti. Ove dvije varijable upravo predstavljaju atribute te klase, odnosno, objekata koji će se instancirati iz nje. Vrijednosti pridružene ovim atributima biti će inicijalno postavljene svim novostvorenim objektima. Kao i u slučaju funkcija, ove varijable su vezane samo za ovu klasu i njene objekte. Ako bismo nakon definicije klase upisali liniju [print(pitanje)], Python bi javio grešku, jer varijabla naziva “pitanje” ne postoji izvan konteksta klase “zadatak”.

Petom i šestom linijom instanciramo dva objekta, naziva “zad1” i “zad2”. Instanciranje se izvodi na priično jasan način: varijabla se izjednačava sa tzv. konstruktorom objekta. Naziv klase iza kojeg se stavljaju zagrade predstavlja konstruktor, odnosno, funkciju koja stvara novu instancu klase, novi objekt. Tako će se izvršavanjem dijela kôda [zadatak()] stvoriti novi objekt klase “zadatak” koji će se zatim pomoću znaka jednakosti pridružiti varijabli zad1, odnosno, zad2.

Sedmom i osmom linijom kôda ispisujemo atribute novostvorenih objekata. Atributu se pristupa pomoću točke, koja predstavlja vezu [roditelj.dijete]. Termini “roditelj” i “dijete” (engl. parent, child) uobičajeni su u opisu odnosa među strukturama, jer zaista, može se reći da je klasa “zadatak” roditelj objektima zad1 i zad2, koji su pak roditelji atributima “pitanje” i “odgovor”. Dakako, svaki objekt je roditelj svojim atributima: zad1 ima dvoje djece, a zad2 ima svojih dvoje djece. Dakako, ovaj odnos može imati i veću razinu dubine; ako bi objekt zad1 bio unutar nekakvog objekta naziva “kviz1”, pitanju zadatka 1 bismo mogli pristupiti na način [kviz1.zad1.pitanje].

python škola primjer 15
Vidimo da je sadržaj atributa jednak onome određenom u definiciji klase. Nakon dodjeljivanja novih vrijednosti atributima objekta zad1, ispisujemo nove vrijednosti oba objekta. Vidimo da su se atributi, kao što je i očekivano, promijenili samo za objekt zad1.
Iako smo tek samo zagrebali površinu mogućnosti koje objektno orijentirani pristup nudi, već se mogu uočiti mnoge prednosti ovakvog pristupa. Naša klasa proizvoda zasad ima samo dva atributa, ništa više.

Naivno bi se dalo zaključiti kako smo mogli upotrijebiti rječnik (dict) s dva ključa. Međutim, valja uočiti koje se sve mogućnosti objekata zasad nude: definiranje pretpostavljene (defaultne) vrijednosti atributima, definiranje atributa bez potrebe za stvaranjem svih objekata odjednom (naime, želimo li imati više rječnika s istim ključevima, moramo ih odmah sve stvoriti ili svaki puta ponovno zadavati ključeve) te ono najvažnije: mogućnost dodavanja metoda i nasljeđivanja atributa iz roditeljskih klasa.
Pojam konstruktor podrazumijeva metodu (funkciju) koja se pokreće pri stvaranju, konstruiranju objekta. Gotovo uvijek želimo pri samom stvaranju objekta postići nekakvo zadavanje inicijalnih vrijednosti, pokretanje provjera i slično. U primjeru 16 imamo primjer definicije konstruktora za objekt klase “zadatak”.

U definiciji zadatka imamo atribut klase “zadatak”, a naziva “broj zadataka”, koji je inicijalno postavljen na nulu. Nadalje, imamo definiciju funkcije, što je obrađeno u prošlom tekstu o Pythonu. Naime, svaka funkcija koja se nalazi unutar definicije klase naziva se metodom. Sintaksa je ista kao u slučaju funkcije, no njena uloga i upotreba je ipak nešto drukčija. Metoda podrazumijeva funkciju koja se poziva nad nekim objektom, dakle, iako je općenito zadana za cijelu klasu, njeno ponašanje se veže za određeni objekt. Iako, dakako, imamo slobodu mijenjati i varijable izvan samog objekta, što ćemo kasnije i vidjeti u ovom primjeru.
Općenito, prvi argument svake metode mora biti rezervirana varijabla naziva “self”. Ova varijabla predstavlja sâm objekt nad kojim se metoda poziva.

python škola primjer 16
Jedina zadana metoda ima naziv “__init__” (pazite, to su dva dvostruka “underscore” znaka). Ovaj naziv je rezerviran za definiciju konstruktora – sve što se navede unutar definicije ove metode bit će izvršeno pri stvaranju svakog objekta. U našem slučaju, konstruktor prima tri argumenta: self, arg1 i arg2. Opisani argument self se ne nikad ne navodi pri pozivanju metode, nego samo pri definiciji. Naime, Python sâm proslijedi metodi objekt nad kojim se ona vrši, a metoda ga prima u obliku varijable self. Ubuduće ćemo za ovakav slučaj govoriti da prima dva argumenta, jer je varijabla self podrazumijevana.
U tijelu konstruktora sada definiramo da se interne varijable naziva “pitanje” i “odgovor” koje su djeca (atributi) objekta nad kojim se metoda vrši inicijalno postavljaju na vrijednosti arg1 i arg2 koje su proslijeđene konstruktoru. Nakon toga, dodatno povećavamo sadržaj varijable brojZadataka unutar klase naziva “zadatak” za jedan.

Glavni dio programa sastoji se od instanciranja dva objekta te ispisa njihovih atributa i atributa klase. Pri instanciranju sada imamo i argumente koje proslijeđujemo konstruktoru. Tako će pri stvaranju objekta zad1 unutar metode __init__, argument self biti upravo objekt zad1, argument arg1 će biti string ‘2+2*2=?’, a arg2 će biti string ‘6’. Jednako tako će pri stvaranju drugog objekta, argumenti poprimiti vrijednosti vezane za taj drugi objekt. Ispisom se jasno može vidjeti da metoda vezana za jedan objekt varijablom self utječe samo na taj objekt.

Zadnjom linijom kôda vidimo što se događalo s varijablom brojZadataka. Ona je vezana za klasu “zadatak”, pa je tako stvaranjem prvog objekta uvećana na vrijednost 1, a stvaranjem drugog objekta na vrijednost 2. Naposlijetku imamo, na vrlo jednostavan način, realiziran brojač stvorenih objekata klase “zadatak”.

U primjeru 17 smo dodavanjem svega dvaju kratkih metoda uvelike poboljšali našu klasu zadataka, koju ćemo lako ugraditi u budući kviz. Općenito, pristup stvaranju kvalitetnih klasa i objekata je često takav da se dobro izdefinira klasa sa svim svojim fukcionalnostima te pohrani u odvojenu datoteku koju onda možemo učitati (engl. include, u slučaju Pythona import) u bilo koju aplikaciju. Ovdje ćemo također težiti takvom pristupu.
Spomenimo prvo metodu naziva “ispisiZadatak”. Metoda jednostavno služi ispisu zadatka i njegovih odgovora koji je usto dobro formatiran i sasvim solidan za ispis zadatka u gotovoj aplikaciji.

Na prvi pogled, za ovu fazu razvoja programa, ova metoda se može učiniti nebitnom i suvišnom. No, vjerojatno ste primjetili da proširivanjem klase ispis objekata postaje iznimno dosadan za kodiranje, jer moramo ispisivati svaki atribut posebno, za svaki objekt posebno. Upravo zbog toga valja što prije definirati vlastitu metodu za ispis, koji ne mora nužno biti lijepo formatiran, no bitno je da je funkcionalan. Definiranjem vlastitog ispisa omogućujemo bolje praćenje događanja unutar objekata, a ujedno si skraćujemo vrijeme potrebno za pronalaženje eventualnih grešaka unutar definicije klase. Metodu za ispis je također dobro pozicionirati na neko specifično mjesto unutar definicije klase, kao što je to u našem slučaju odmah iza konstruktora, kako biste brzo i jednostavno mogli dodavati ispise novouvedenih varijabli tokom izgradnje vlastite klase.

python škola primjer 18
Što se tiče konstruktora, malo smo ga modificirali čime smo ujedno modificirali i građu samog objekta. Argumente smo preimenovali u smislenija imena, a usput smo i promijenili funkciju drugog argumenta – on će nam odsad predstavljati broj bodova koji točno riješen zadatak donosi. Dobro je znati i to da se imena argumenata koja se koriste u definiciji metode (a i funkcije) upotrebljavaju u IDE-ima koji podržavaju tzv. Code Completition (dovršavanje kôda) na način da programer početkom upisivanja funkcije odmah vidi koje argumente treba unijeti pri pozivu iste. Novodefiniranim konstruktorom, dakle, unosimo pitanje i broj bodova, te definiramo odgovore kao prazan niz, u koji ćemo onda posebnom metodom unositi odgovore koji će biti ponuđeni pri ispisu zadatka u kvizu.

Preskočimo zasad metodu za ispis te pogledajmo što radi metoda.dodajOdgovor(). Za početak, ona prima jedan ili dva argumenta (prisjetimo se funkcija i predefiniranih argumenata). Prvi argument je tekst odgovora, dok je drugi točnost odgovora, koja je po “defaultu” lažna. Logika nam nalaže da ćemo dodavajući odgovore za pojedini zadatak uvije imati više netočnih nego točnih ponuđenih odgovora, pa ovime postižemo da ukoliko ne predamo drugi argument, odgovor je netočan. Dakle, skraćujemo vrijeme programiranja. Metoda jednostavno nizu odgovori unutar aktivnog objekta dodaje član koji je zapravo lista od dva člana: prvi član je tekst odgovora, a drugi je točnost. Naposlijetku, iako nije nužno, imamo funkciju return(self), koja kao rezultat izvršavanja vraća upravo aktivni objekt. Ovime omogućujemo ulančavanje metoda, što ćemo vidjeti kasnije u ovom primjeru.
Metodu za ispis naziva “ispisiZadatak” smo odmah izveli tako da dobijemo lijepi ispis zadatka s brojem bodova i numeriranim ponuđenim odgovorima. Metoda ne prima nijedan argument, osim aktivnog objekta, jer dodatni argumenti jednostavno nisu potrebni.

Kasnije možemo eventualno uvesti argumente za izmjenu načina ispisa i slično. Sadržaj metode se svodi na “igrariju” s atributima i zbrajanjem stringova: prvo ispisujemo broj bodova i pitanje, a zatim pomoću for petlje iteriramo kroz sve odgovore aktivnog zadatka. Upravo zbog jednostavnijeg numeriranja odgovore smo pohranjivali u niz, a ne npr. u rječnik, jer da smo koristili rječnik, programerski bi bilo zahtjevno izvesti jednostavno korisnikovo odabiranje odgovora.
Ovako će korisnik odabirati odgovor jednostavnim upisom rednog broja i pritiskom na tipku Enter. Ova metoda također kao rezultat vraća objekt nad kojim se izvršava.
Sada tek vidimo u glavnom dijelu programa beneficije korištenja objekata u ove svrhe. Prvom linijom glavnog dijela programa instanciramo jedan zadatak, s tekstom zadatka i brojem bodova. Drugom linijom dodajemo tri ponuđena odgovora, pri čemu smo za prvi odredili da je točan (ostali su po defaultu netočni). Prvi poziv metode za dodavanje odgovora dodaje odgovor te kao rezultat vraća upravo objekt zad1, što smo omogućili linijom [return(self )]. Zbog toga, jednostavno na prvu metodu nadovezujemo drugu, koja se poziva opet na objektu zad1, te naposlijetku treću. Primjetite da smo mogli u isti “lanac” povezati i metodu za ispis, koju smo ipak zbog preglednosti odvojili u sljedeću liniju kôda.
Sljedeći primjer predstavlja završenu klasu zadatka koja je ujedno spremljena u vlastitu datoteku naziva zadatak.py. Dodane su tri nove metode: jedna za provjeru točnosti odgovora i dvije za miješanje odgovora, odnosno, postavljanje u početni redoslijed. Kako biste vidjeli rezultat izvršavanja pet linija kôda glavnog dijela programa, jednostavno ih odkomentirajte brisanjem vodećih znakova #.
Prva linija kôda je [from random import shuffle]. Ova linija govori: “IZ MODULA random UČITAJ shuffle”. Ovom linijom kôda učitavamo funkciju shuffle() koja nam je potrebna za miješanje odgovora. Na sličan način ćemo klasu zadatak iz datoteke zadatak.py kasnije učitavati u glavni program.

 

Iz klase smo izbacili brojač zadataka, jer ćemo njega kasnije implementirati izvan klase zadataka. Osim dodanih metoda, promjenu u klasi predstavlja dodani niz naziva “indeks”, preko kojeg ćemo dohvaćati odgovore. Naime, dodavanjem odgovora, ovaj niz se puni na način da će na nultom mjestu imati broj 0, na prvom mjestu broj 1 itd., a imat će članova točno onoliko koliko ima odgovora. Ovo se postiže drugom linijom metode.dodajOdgovor(). Niz indeks služi nam kao filter kroz koji dohvaćamo odgovore. Pri ispisu zadatka i provjeri točnosti provlačimo redni broj odgovora kroz taj filter. Novododanom metodom.pomijesaj() pomoću funkcije shuffle() članovi u nizu indeks se ispremiješaju, pa ako je recimo inicijalno bio zadan kao [0,1,2,3], sada će npr. biti [1,3,2,0]. Tako pri dohvaćanju odgovora, dohvatit će se npr. umjesto nultog člana prvi član, umjesto prvog člana treći član itd.

Metodom.poslozi() jednostavno se niz indeks vraća na normalne vrijednosti:

funcija range() generira uzlazni niz brojeva te sprema u varijablu indeks.
Metoda.provjeriTocnost() provjerava točnost odgovora koji su joj predani kao argument. Naime, korisnik će upisivati odgovore u obliku “2” ili “1,3”, pa će se taj string proslijeđivati u ovu metodu. Na samom početku metode, varijabla i se iterira kroz taj string pretvoren u listu (“1,3” postat će [1,3]).
Provjerava se sljedeće: je li False u nizu odgovori na mjestu i-1 na mjestu 1, s tim da je mjesto i-1 filtrirano kroz niz indeks. Od i se oduzima jedan, jer ako su ponuđena tri odgovora, korisnik vidi brojeve 1, 2 i 3, a njihova mjesta u nizu su 0, 1 i 2 (ovo je postignuto u metodi za ispis zadatka). Ako je False, jedan od korisnikovih odgovora je netočan te metoda vraća 0 (za postignute bodove). Ako se petlja izvrti do kraja, svi ponuđeni odgovori su točni te se vraća broj bodova pridjeljen tom zadatku, ukoliko je točno riješen.
Sad kada imamo u potpunosti dovršenu klasu zadatka možemo početi graditi klasu kviza. Iako bi se kviz mogao izvesti bez stvaranje nove klase, ovakav pristup nam je pogodniji, jer će nam omogućiti dodavanje različitih metoda npr. odabir pitanja koja će se prikazati, miješanje redoslijeda pitanja, računanje postignutih bodova, učitavanje seta pitanja iz datoteke itd. Ne treba ni spominjati da stvaranjem klase kviza možemo instancirati proizvoljan broj odvojenih kvizova.
Iako primjer u kojem je sadržana klasa kviz djeluje opsežan, on to zapravo nije. Ako pažljivije pogledate, uočit ćete da povelik dio kôda predstavlja upis i ispis podataka. Na samom početku učitavaju se klasa zadatak i funkcija za miješanje niza. Klasa kviz, uz konstruktor sadrži svega tri metode: prva služi podešavanju ponašanja kviza, druga je za unos podataka i treća za pokretanje kviza.
Unutar konstruktora imamo prvo niz zadaci. U njemu će se instancirati svi zadaci koji se stvore tokom stvaranja kviza. Varijable bodovi i postignuti sadrže informacije o maksimalnom broju bodova u kvizu i broju postignutih bodova. Pokretanjem metode.podesiKviz popunjava se rječnik opcije: ključ ‘broj’ sadrži broj zadataka koji će se prikazivati (kviz može npr. imati 10 zadataka, a da se samo tri prikazuju), opciju miješanja pitanja i opciju miješanja odgovora. Pri izvršavanju kviza ove će se opcije provjeravati te će se kviz u skladu s njima izvršavati.
python škola primjer 18
Metoda.unesiPitanja jednostavno služi za korisnikov unos pitanja, bodova, odgovora i točnosti odgovora. Ukoliko korisnik pri unosu pitanja ili odgovora upiše riječ “exit”, metoda prestaje s učitavanjem pitanja odnosno odgovora. Nadalje, pri unosu pitanja i bodova odmah se u varijablu imena “zadaci” aktivnog objekta klase kviz dodaje novi objekt klase zadatak. Nakon toga se učitavaju odgovori jedan po jedan (do unosa riječi “exit”) te se svaki od njih pomoću metode zadnjeg zadatka u nizu zadataka aktivnog kviza dodaje tom zadatku.
U metodi.zapocniKviz() prvo se provjerava opcija miješanja pitanja. Ako je uključena, pomoću već spomenute funkcije shuffle() objekti zadataka unutar niza imena “zadaci” se izmiješaju. Nakon toga se iterira po zadacima, ali ne svim, nego kroz onoliko zadataka koliko je odabrano pri podešavanju kviza. Ispituje se opcija za miješanje odgovora te se u skladu s njom postupa, nakon čega se bezuvjetno maksimalni broj bodova povećava za broj bodova aktivnog zadatka. Pokreće se metoda za ispis zadatka, a nakon korisnikovog unosa odgovora provjerava se točnost. Ispisuje se adekvatna poruka o točnosti zadatka te se zbrajaju bodovi postignuti na sktivnom zadatku s ukupnim brojem postignutih bodova.
Na kraju vidimo kako je jednostavno pokrenuti kviz – zbog vraćanja varijable self unutar metoda jednostavno nad instanciranim kvizom pokrenemo sve tri metode te dobijemo cjeloviti program.
python škola primjer 19

Zbog objektno orijentiranog pristupa moguće je vrlo jednostavno dodati nove metode kojima se može povećati funkcionalnost kviza. Lako se može postići i hvatanje grešaka te ispisivanje adekvatnih poruka (npr. ako pri odabiru opcije korisnik nije unijeo znak Y ili N i slično).

U posljednjoj fazi razvoja kviza dodat ćemo metodu za učitavanje pitanja i odgovora iz datoteke, jer je nepraktično svaki puta ponovno vršiti unos istih.
U okviru sa strane možete vidjeti kako izgleda format datoteke koji ćemo koristiti za pohranu pitanja i odgovora. Dodajemo postojećoj klasi metodu.ucitajPitanja() čijim pokretanjem se od korisnika traži unos imena, odnosno, putanje datoteke iz koje će se učitati pitanja, odgovori, bodovi i točnost.
Datoteka je formatirana na sljedeći način: Svako pitanje završava znakom # te brojem bodova iza znaka. Do sljedeće takve linije su odgovori vezani za to pitanje. Točni odgovori završavaju znakom #, dok netočni ne sadrže taj znak.
Ovakvim formatom se postiglo relativno jednostavno učitavanje podataka iz datoteke. Kod osmišljavanja formata bitno je paziti na unikatnost formata različitih cjelina. Tako su ovdje međusobno različiti formati za pitanje, točan odgovor i netočan odgovor. Naime, osnova rada ove metode je upotreba metode.split(‘#’) nad svakim učitanim retkom datoteke.
U slučaju pitanja i točnog odgovora dobivamo listu od dva člana, pri čemu u slučaju pitanja drugi član liste ima neku duljinu (sadrži broj bodova) dok je drugi član točnog odgovora prazan string. Preostali slučaj je netočan odgovor, kod kojeg.split(‘#’) vraća listu od jednog jedinog člana, jer nema znaka ‘#’.
Na početku metode u varijablu imena “datoteka” učitava se datoteka pomoću funkcije open(). Ova funkcija prima dva argumenta: prvi je putanja do datoteke (u našem slučaju korisnikov unos), a drugi je mod rada s datotekom (čitanje, pisanje, oboje itd.). Mi ovdje samo čitamo datoteku, pa je drugi argument string ‘r’ (kratica od engl. read, čitati). Objekt tipa datoteke (file) među ostalim podržava metodu.readlines(). Metoda vraća sve retke datoteke odvojene u listu redaka, dakle svaki redak datoteke će zauzeti jedno mjesto u listi.
Ulaskom u petlju varijabla linija sadrži redak datoteke. Nad varijablom prvo vršimo metodu.strip() koja uklanja vodeće i prateće praznine (razmak, tab, prelazak u novi redak). Ovo je dobra praksa, jer često ne možemo biti sigurni kakve je oznake kraja reda koristio editor u kojem smo kreirali datoteku. Nakon “stripanja”, u tu istu varijablu spremamo listu dobivenu pomoću spomenute metode.
split(‘#’) te ispitujemo također spomenute uvjete: prvo ima li novodobivena lista dva člana, a zatim ako ima, je li zadnji član prazan string. Adekvatno rezultatima ispitivanja uvjeta poduzimaju se spomenute radnje vezane za stvaranje zadataka i odgovora. Nakon cjelokupne obrade metodom.close() se zatvara datoteka koju smo otvorili. Nakon dodavanja metode u klasu, kviz se može jednostavno pokrenuti pomoću [kviz1 = kviz()], a zatim [kviz1.ucitajPitanja().podesiKviz().zapocniKviz()].
Vidimo da s programerske strane nije kompliciranije i vremenski dulje izvesti čitanje iz datoteke umjesto čitanje s tipkovnice. Jedan od razloga je očit – za razliku od korisnika, datoteci ne moramo ispisivati poruke o unosu podataka.
Ovime smo zaokružili osnove korištenja objekata i klasa u Pythonu. Primjetite kako je korištenje objekata intuitivno i lako te pruža mogućnosti proširenja funkcionalnosti bez puno muke.
Dodatno, objekti u Pythonu su na neki način jednostavniji za korištenje od onih u drugim programskim jezicima zbog Pythonove izuzetno jednostavne sintakse.
Autor: Josip Užarević

RELATED ARTICLES

2 Komentari

Komentiraj

Please enter your comment!
Please enter your name here

- Advertisment -

Most Popular