2. OSNOVNI ELEMENTI PYTHON-a

2.1. Uvod

 

Python je stvoren kao reakcija na, i poboljšanje kompajliranih jezika poput C++ i Java, te skrip jezika poput Perl-a. Poseduje mešavinu odlika nekoliko jezika – čak funkcionalnih jezika – i dizajniran je da stavi najbolje karakteristike od svakog jezika u jezik koji bi mogao podržavati tip brzog razvijanja koji su skript jezici u mogućnosti da podrže. Filozofija dizajna je bila fokusirana na pouzdanost koda.


Ovo znači da Python podržava višestruke programske stilove i paradigme. On je primarno imperativni jezik i programi mogu biti napisani u celosti iz imperativne perspektive. Python podržava objektnu-orjentisanost u svom jezgru i podržava struktuirano programiranje. Međutim, postoji takođe podrška za funkcionalno programiranje.


Python kombinuje najbolje delove programskih jezika u jezik visoke čitljivosti koji se izvršava u skriptovanom okruženju. Pod pojmom “skriptovanom”, misli se da se Python programi izvršavaju direktno od strane “izvšioca”, bez prevođenja na izvorni mašinski kod. Pod “izvršiocem” se misli na “interpreter”; on interpretira Python izjave čitajući i frazirajući ih i izvršava te interpretacije izvršavajući mašinski kod na hardware-u host računara. Kada se uporedi sa jezicima poput C++ koji kompajliraju na mašinski jezik, Python interpreter predstavlja dodatni sloj izvršenja, koji može biti problem performansi programa. Interpretirani Python programi se obično izvršavaju sporije od sličnih programa napisanih u kompajliranim jezicima. Međutim, mnogo je urađeno na Python interpreterima da se što više smanje problemi.


Skriptovana priroda Python-a ima mnoge prednosti. Interpreteri mogu zauzeti mnoge forme, što znači da se Python može koristiti na mnogim mestima. Može postati deo drugih software-skih sistema, poput web browser-a ili sistemskih kontrolera, a može se i prebacivati na različite arhitektonske platforme sa lakoćom. Python podržava eksperimente sa jezičnim karakteristikama, jer njegovo okruženje može biti nadograđeno sa novim plugin programima.


Svi od mnogih Python-ovih aspekata dizajna će postajati očitiji kako budemo predstavljali Python programiranje.

 

2.2. Varijable (Promenljive)

 

Jedna od prednosti Python-a je sposobnost da apstrakuje koncept računarske memorije u nešto što se lako upotrebljava: ideja varijabli. U Python-u, varijable sadržavaju objekte i objekti predstavljaju podatke. Varijable imaju identifikatore ili imena. Imena moraju počinjati sa slovom, ali mogu imati bilo koju kombinaciju slova, brojeva i “_” karaktera. Python koristi dodeljivanje da dodeli varijabli vrednost i referencira vrednost varijable kada se varijabla upotrebi imenom.


Python ne zahteva da varijable budu deklarisane i ne pruža mehanizam za isto – jednostavno dodeljivanje vrednosti nekoj varijabli čini da ona postoji (ovo je značajna razlika između Pythona i kompajliranih jezika poput C++ i Jave). Stoga, možemo dodeliti vrednosti poput:

 

>>> hours = 42.5
>>>
hourly_wage = 12
>>> payment = hours * hourly_wage
>>> payment
510.0

 

Korišćenje promenljive bez da joj je prethodno dodeljena vrednost je greška. Razmotrimo pokušaj da se upotrebi promenljiva prekovremeno bez dodele vrednosti:

 

>>> payment = hours * hourly_wage + overtime * hourly_wage * 1.5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'overtime' is not defined

 

2.3. Osnovni tipovi promenljivih

 

Druga prednost Python-a je sposobnost da gleda na podatke na mnogo različitih načina – kao brojeve, string-ove, ili pixel-e slike. Kao i u drugim programskim jezicima, promenljive u Python-u su tipizirane, tj. klasifikovane su na osnovu vrednosti koju mogu uzeti i operacijama koje se njima mogu izvršiti. Tipovi koji su toliko osnovni za programiranje u Pythonu su built-in, tj. ugrađeni se nazivaju jezgreni tipovi. Python ima 11 ugrađenih tipova promenljivih, koje su sumirane u narednoj tabeli (Tabela 1) .

 

Tabela 1 . - Python Built-in Tipovi

 

Tip objekta

Opis

Primeri

int

Ceo broj za fiksiranom preciznošću

19, 72, -12

float

Broj sa pokretnom tačkom

3.14159, -50.309, 100.2

complex

Broj sa realnim i imaginarnim delovima

7+4.5j

boolean

Istinska vrednost

True, False

string

Sekvenca Unicode karaktera

'mobile', "Python's"

byte

Sekvenca od 8-bit bajtova

b'one', b"characters"

list

Sekvenca objekata, koja može biti sačinjena od mešanih tipova

[7,"lowest",False]

tuple

Sekvenca objekata, koja može biti sačinjena od mešanih tipova

(7,"lowest",False)

dictionary

Grupa key/value parova, koja može biti sačinjena od mešanih tipova

{"key1":17,
"key2":True}

set

Neuređena sekvenca objekata, koja može biti sačinjena od mešanih tipova, bez duplikata

{7,"lowest",False}

file

Reprezentacija podataka koji se čuvaju u memoriji

myFile = open("README",'r')

 

Neki od ovih tipova su već poznati: int, float, boolean, string, i čak complex tip postoji u mnogim programskim jezicima. No, postoje neki tipovi koje je potrebno dodatno pojasniti:


Byte tip je predstavljanje 8-bitnih kvantiteta. Karakteri staju u 8-bitne celine (ASCII karakteri da, UNICODE karakteri ne) i Python predstavlja bajtove kada nakon karaktera ‘b’ usledi string. U tabeli (Tabela 1 ), b’one’ je sekvenca od tri bita:01101111, 01101110, i 01100101.


List i tuple tipovi se mogu posmatrati kao kolekcije, poput nizova i vektora u drugim programskim jezicima. Zapravo, oni su poput kombinacije struktura i nizova iz C i C++, i mogu sadržati bilo koji tip podataka u sebi. U tabeli (Tabela 1 ), možemo videti integer, string i boolean u svakoj kolekciji. Lists-e su predstavljenje korišćenjem kockastih zagrada, tuples su predstavljeni korišćenjem običnih zagrada.


Razlika između listi i tuples-a je u njihovoj nepromenjivosti. Nepromenjivi objekti u Python-u se ne mogu menjati. Tuples-i (i string-ovi) su u Python-u nepromenjivi. Liste su podložne promeni.

 

Na primer, možemo dodeliti listu vrednosti promenljivoj, promeniti listu i ispisati je:

 

>>> thelist = ['trees', 'birds', 'flowers']
>>> thelist
['trees', 'birds', 'flowers']
>>> thelist[1] = 'cows'
>>> thelist
['trees', 'cows', 'flowers']

 

Ovde smo u mogućnosti da promenimo stavke u listi koristeći zapis sličan nizu. Ako bismo to pokušali za tuple-om, dobili bismo grešku:

 

>>> thetuple = ('trees', 'birds', 'flowers')
>>> thetuple
('trees', 'birds', 'flowers')
>>> thetuple[1] = 'cow'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

 

Ovaj koncept nepromenjivosti se može koristiti da bi se zagarantovala da objekti ostaju konstantni kroz ceo program. Nepromenjivost prisiljava programere da dodeljuju nove vrednosti novim promenljivama, umesto da menjaju vrednosti postojećih.


Dictionary u Python-u je mapping – nalik je hash table tipu u Javi. Elementi u dictionary-ju su sledišteni u key/value parovima. Promenljive koje sadržavaju dictionary objekte mogu se referencirati sa key i vratiti vrednost. Razmotrimo sledeće primere:

>>> thedictionary = {"item": "coffee", "onshelf": True, "quantity": 10}
>>> thedictionary["item"]
'coffee'
>>> thedictionary["quantity"] += 1
>>> thedictionary["quantity"]
11

 

Primetimo da su keys korišćeni za referenciranje svakog elementa, ali da je vrednost vraćena; i da, nasuprot nizovima, dictionary­ mogu biti referencirani sa bilo kojim tipom.

 

Sets su skup objekata koji podržavaju standardni matematički set manipulacijskih operacija: unija, presek i razlika. Razmotrite sledeće primere:

 

>>> theset = set(['t', 'r', 'e', 'e'])
>>> theset
set(['r', 'e', 't'])
>>> anotherset = set('flower')
>>> theset & anotherset
set(['r', 'e'])
>>> theset | anotherset
set(['e', 'f', 'l', 'o', 'r', 't', 'w'])
>>> theset - anotherset
set(['t'])
>>> 't' in theset
True

 

Presek je predstavljen sa ‘&’ operatorom, unija sa ‘|' operatorom i razlika sa '-' operatorom. Operator 'in' se koristi da testira pripadnost. Primetite da sets ne sadrže duplirane elemente.


Python je ‘dinamički kucan’, ovo znači da se tip promenljive može promeniti ukoliko se promeni vrednost koja joj je dodeljena. Mnogi drugi jezici su ‘statički kucani’, tj. tip svake varijable je definisan kad se varijabla deklariše. Možemo pomisliti da dinamično kucanje može postati zbunjujuće, ali svaki tip u Python-u ima jedinstvenu notaciju (zapis). Na primer, liste se specifikuju sa pravougaonim zagradama, tuples sa običnim zagradama, a sets sa ‘set’ funkcijom.

 

 

2.4. Operacije

Svaki tip podataka u Python-u ima dve osobine: vrednosti korišćene za tip i operatori koji se mogu koristiti sa vrednostima tog tipa. Ispod se nalazi kratak pregled operatora koji se mogu koristiti sa brojevnim tipovima, string-ovima i sekvencama.
Celobrojni i brojevi sa pokretnom tačkom koriste standardne aritmetičke operatore. Pythom uključuje ‘**' operator za eksponenciju.
String-ovi i ostale sekvence podržavaju povezivanje u celinu ('+' operator) i ponavljanje ('*' operator). Razmotrimo sledeće primere:

 

>>> listing = [1, 2, 3, 4, "DONE"]
>>> listing + listing
[1, 2, 3, 4, 'DONE', 1, 2, 3, 4, 'DONE']
>>> listing * 3
[1, 2, 3, 4, 'DONE', 1, 2, 3, 4, 'DONE', 1, 2, 3, 4, 'DONE']

 

Tipovi u Python-u koji uključuju sekvence su okarakterisane sekvencijalnim ređanjem. Sekvence se referenciraju koristeći pravougaone zagrade da bi se ispitali pojedinačni elementi svake sekvence. Organizovane su sa leva na desno. Referentne notacije su pomeraj od najlevljeg položaja. Python dozvoljava neke specijalne notacije koje su prečice, kao što je prikazano ispod:

 

>>> drsuess = 'The Cat in the Hat'
>>> animal = drsuess[4:7]         # izvadi cat
>>> animal
'Cat'
>>> onmyhead = drsuess[15:]       # pribavi Hat
>>> onmyhead
'Hat'
>>> lastone = drsuess[-1]         # poslednji karakter
>>> lastone
't'

 

Izvadci iz sekvence se nazivaju isečci (slices) – za string-ove, možda su vam poznati kao substrings. Reference sa negativnim indexom se odnose na kraj sekvence.
Sekvence takođe mogu sadržavati i druge sekvence, kao što je prikazano ispod.

 

>>> drsuess = ["Horton Hears a Who", "Green Eggs and Ham"]
>>> drsuess[1][0:5]
'Green'
>>> drsuess[1][0:5][2]
'e'

 

Važno je zapamtiti da su string-ovi samo sekvence. U ovom primeru, promenljiva drsuess sadrži sekvencu od dva elementa; od kojih su oba sekvence. Dvostruka referenca drsuess[1][0:5][2] izvlače drugi element u sekvenci drsuess i onda izvlači karaktere od 0 do 4 iz te sekvence, vraćajući vrednost Green.

 

2.5. Izjave (Statements)

U Python-u, kao i u drugim jezicina, ‘izjava’ je izvršni element u jeziku. To je element jezika koji sprovodi akciju programa.


Sintaksa izjava u Pythonu je pomalo drugačiju u poređenju s drugim jezicima. Razmotrite sledeću Java izjavu:

 

while (x < y) {
   x += 2;
   y = y - 2;
}

 

Ova izjava while petlje ima blok izjava, telo petlje, okruženo sa "{" i "}". Izjave unutar petlje se završavaju znakom “;”. Čitav set izjava je fleksibilan. Mogao bi biti u jednoj liniji jer se razmaci ignorišu u Java kompajleru.

 

U Python-u, sintaksa je čistija, ali i više ograničavajuća. Razmotrimo ekvivalentnu Python while petlju.

 

while x < y:
   x += 2
   y = y - 2

 

Kod je čistiji, zagrade i “;” nisu potrebni u Python-u. Međutim, kao posledicu imamo kod koji više ograničava. Blok izjava formiran pomoću zagrada u Java-i se ovde formira uvlačenjem reda (indentacija). Python se oslanja na indentaciju za formiranje blokova izjava. Pojedinačne izjave se moraju pojavljivati u pojedinačnim redovima, osim ako se koristi “;”.  Konačno, primetite dvotačku (“:”), koja se koristi kao terminator while izjave; dvotačke se koriste kada se očekuju blokovi koda.
Možete koristiti tačku-zarez (“;”) da odvojite jednu izjavu od druge. Kod prikazan ispod je identičan kodu koji se nalazi iznad, premda ne izgleda toliko čisto:

 

while x < y: x += 2; y = y - 2

 

Razmotrimo najjednostavniju izjavu u Python-u. To je ‘pass’ izjava. Ne radi ništa, ali se često koristi kao punjač za praznu blok izjavu. U jeziku poput Java-e, prazan blok je prosto prazan par vitičastih zagrada: {}. Međutim, u Python-u, prazan blok je uvučen prazan red, koji je nevidljiv, pa se stoga koristi 'pass'. Na primer, prazna while petlja bi mogla izgledati ovako:

 

while x<y:
   pass

 

2.6. Izrazi i dodele

Izraz je kombinacija reference objekta i operatora. Izrazi stvaraju i rukuju sa objektima. Razmotrimo sledeći izraz:

 

x + 15 * L[2]

 

Promenljiva x je referencirana i ta referenca stvara objekat čija se vrednost i tip podatka poklapa sa podatkom sadržanim u x. Upotreba broja 15 u izrazu tera Python da kreira integer objekat čija je vrednost 15. Referenca liste L[2] izvlači treći element u kolekciji nazvanoj L i kreira još jedan objekat. Sva tri novokreirana objekta su bezimeni.


U Python-u, navedeni izraz je validna izjava: možete koristiti izraze kao izjave. Svi izrazi vraćaju vrednost, osim ako izraz ima nuspojavu, te vrednost bude odbačena. U navedenom izrazu ne dolazi do nuspojave (jer nijedna funkcija nije pozvana), te vrednost biva izračunata i zanemarena.


Dokle god sadrže operatore validne za tip podataka sa kojim rade, izrazi mogu obuhvatiti bilo koju kombinaciju objekata i operatora. Sledeća tabela ilustruje neke uobičajne izraze i njihovu interpretaciju (Tabela 2).

 

Tabela 2 . – Uobičajeni izrazi i njihovo tumačenje

 

Izrazi

Tumačenje

x + 15 * L[2]

Aritmetički izraz

a < b

Boolean izraz

x + 15 * L[2] < 100 and a < b

Spojeni boolean izraz

a < b < c

Kombinacija boolean izraza

f(x,y)

Poziv funkcije

 

Ovo bi trebalo biti poznato ukoliko poznajete kako drugi jezici rukuju sa izrazima. Pravila prvenstva važe u Python-u kao i u drugim jezicima. U prvom izrazu, operator * će se izvršiti pre operatora +. Boolean kombinacioni operatori se speluju (and, or i not), umesto da se definišu specijalnim simbolima kao u Java-i i C++.


Dodeljivanje u operaciji menja vrednost objekta varijable. Sintaksa izgleda isto kao u drugim jezicima:
promenljiva = izraz


Izraz prvo bude izračunat i rezultirajući objakat dodeljen naznačenoj promenljivoj. Evo česte greške u Python-u koja uključuje pozivanje funkcija, što su validne izjave. Razmotrimo naredni kod:

 

>>> aList = [10,20,30]
>>> aList.append(40)
>>> print aList
[10, 20, 30, 40]

 

Ovo funkcioniše kao što bi se dalo očekivati. Izraz aList.append(40) je izraz, ali ipak validna izjava. Jednostavno vraća vrednost None koja biva zanemarena. Česta je greška misliti da ovaj izraz može biti dodeljen novoj listi, poput ove:

 

>>> aList = [10,20,30]
>>> aNewList = aList.append(40)
>>> print aNewList
None

 

Umesto toga, nova lista dobija vrednost None, koja je povratna vrednost funkcije pozvane u izraz.  Naredba print ispisuje izračunati rezultat na ekran, ali o tome kasnije.

 

2.7. Uslovne izjave (Kondicionali)

Svi programski jezici sadrže svojsvo izvršavanja uslovnih izjava; Python nije izuzetak. Python-ova if izjava ima svojstvo mnogostrukog grananja.
Uobičajeni format ove izjave se nalazi ispod:

 

if <condition1>:
   <statementset1>
elif <condition2>:
   <statementset2>
else:
   <statementset3>

 

Čak i bez ikakvog poznavanja Python sintakse, izgleda vrlo slično if izjavama u mnogim drugim jezicima. Ovde, <condition1> je izraz koji daje boolean rezultat. Ukoliko je rezultujuća izjava True, izvršava se <statementset1>. Ukoliko je rezultat False, sledeći uslov se ispituje itd. Ako svi uslovi budu False, default blok <statementset3> koji se nalazi iza else ključne reči će se izvršiti. Dvotačke nakon svakog uslova i uvlačenje (indentacija) skupova izjava predstavljaju Python standarde.

 

Evo primera:

 

theString = "The Mad Hatter"
length = len(theString)
if length < 10:
   category = "short"
elif length < 20:
   category = "medium"
elif length < 30:
   category = "kind of long"
else:
   category = "long"

 

U ovom primeru, prvi uslov je False, jer String ima dužinu 14, ali je drugi uslov True. Stoga promenljiva category uzima vrednost medium. Preostali uslovi se ne ispituju.

 

Boolean izrazi u Python-u slični su onim u drugim jezicima. Koriste standardne odnosne operatore (npr. “<” i “>”) i boolean operatore and, or i not. Boolean promenljive mogu se koristiti u boolean izrazima. Na primer, sledeći kod je validan:

 

theString = "The Mad Hatter"
length = len(theString)
toolong = length > 40
printable = not toolong and length > 10
if printable:
   print "The string is OK"

 

I druge vrednosti osim boolean su validne u boolean izrazima. Kao i u drugim jezicima, mogu se koristiti numeričke vrednosti u boolean izrazima. Kada se broj koristi sam, Python ga prevodi u izraz ‘broj != 0’. Stoga je sledeći kod validan:

 

theString = "The Mad Hatter"
length = len(theString)
if length:
   print "The string has characters"

 

Numerička vrednost može biti celobrojna ili broj sa pokretnom tačkom. Zapravo, ideja da se ne-boolean-i smatraju True se proteže na druge slučajeve:
Ne-prazni objekti se smatraju istinitim (True)
Nula numeričke vrednosti i prazni objekti se smatraju neistinitim (False)
Specijalna vrednost None se smatra neistinitom (False)

 

Stvari se komplikuju kada se kombinuju ne-boolean vrednosti sa boolean operatorima. Python koristi kratako-spajanjeda evaluira boolean izraz. Drugim rečima, kada se rezultat može predvideti, Python prekida evaluaciju. Na primer, boolean izraz False i x == 2 može prestati odmah nakon False, jer False i ‘bilo_šta’ jeste False. Python prekida evaluaciju i vraća prvi objekat koji vidi da može predvideti rezultat izraza.


Razmotrimo kod u sledećem primeru:

 

theString = "The Mad Hatter"
length = len(theString)
value1 = length or theString == "The Mad Hatter"
value2 = theString == "The Mad Hatter" or length
value3 = 0.0 or theString == "Mad Hatter"

 

U ovom kodu, value1 uzima vrednost 14. Ovo se događa jer je length ne-nula i stoga True, čineći boolean izraz istinitim (True). Python prestaje da ispituje i vraća prvi objekat koji čini izraz istinitim. value2 uzima vrednost True, jer je upoređivanje prvi True objekat u izrazu i to čini ceo izraz istinitim. Promenljiva value3 uzima vrednost False.


Konačno, u Python-u su, kao i u C++ i Java-i, moguće inline if izjave. Njihova forma je malo drugačija. Uzmimo ovu jednostavnu if izjavu:

 

if x < 10:
   x = 1
else:
   x = -1

 

Inline oblik ove izjave bi bio:

 

x = 1 if x < 10 else -1

 

2.8. Petlje (Loops)

Iterativni algoritmi se rukuju pomuće dve glavne loop-konstrukcije u Python-u: while petlje i for petlje.

 

2.8.1. While petlje

while petlja u Python-u je ista kao i u ostalim programskim jezicima, te dodaje Python-ov dodatak sa boolean uslovima. Opšti format sintakse izgleda ovako:

 

while <condition>:
   <statementset1>
else:
   <statementset2>

 

Osnovno ponašanje while petlje je da izvršava <statementset1> dok je <condition> tačan. No, mnogi različiti oblici petlji su mogući. Beskonačne petlje su jednostavne:

 

while True:
   print "Help! I'm running away..."

 

Većina petlji zavisi od relacijskih operatora:

 

while x < y + 2:
   x += 1
   y -= 1
 
while a**2 < b**2 and c+d == e:
   a = c - d
   b += 1
   c -= 2

 

Možda su vam poznate break i continue izjavu u drugim jezicima. Python ih takođe sadrži. break izaziva zaustavljanje trenutne iteracije kod break izjave i okončava izvršavanje petlje u potpunosti. continue zaustavlja trenutnu iteraciju i dozvoljava da se petlja nastavi izvršavati. Razmotrimo sledeće primere:

 

a = 20
while a:
   a -= 1
   if a % 2 == 0: continue
   print a

 

Ovaj kod ispisuje sve neparne brojeve od 20 pa sve do 0. continue izjava zaustavlja iteraciju tako da se print izjava ne izvršava za parne brojeve.

 

while a**2 < b**2:
   if c+d != e: break
   a = c - d
   b += 1
   c -= 2

 

Ovaj kod radi jednostavno kao i prethodni. Kada c+d != e, petlja se zaustavlja.

 

else deo while petlje je retkost u Python-u, kao i u drugim jezicima. Opciona je. Ukoliko se uključi, izvršava se kada uslov petlje zauzme vrednost False i petlja treba da se završi. Izvršava se isključivo kada se while petlja okončava normalnim putem. Kada se petlja okonča break izjavom, else se ne izvršava. Razmotrite sledeće primere:

 

number = int(raw_input("Enter number: "))
count = 0
while number:
   count += 1
   total = total + number
   number = int(raw_input("Enter number: "))
else:
   print "The average is ", total/count

 

Ovaj kod isčitava brojeve dok se ne unese nula, onda ispisuje prosek i izlazi.

 

2.8.2. For petlje

for petlje su korisne kada želite izvršiti iteraciju nad određenim skupom objekata – na primer, rasponom celobrojnih brojeva ili listom. Sintaksa za for petlju uopšteno izgleda ovako:

 

for <index> in <objectsequence>:
   <statementset1>
else:
   <statementset2>

 

Kao i u drugim jezicima, for petlja se izvršava dodeljivanjem objekta iz <objectsequence> <index>-u  za svaki korak iteracije petlje. Na primer:

 

thesum = 0
for x in [1, 2, 3, 4, 5, 6]:
   thesum += x
print "Average is ",thesum/6

 

Izlaz je: “Average is 3”


Ovo bismo mogli napisati i pomoću tuple-a umesto liste.

 

thesum = 0
for x in (1, 2, 3, 4, 5, 6):
   thesum += x
print "Average is ",thesum/6

 

Bilo koja sekvenca funkcioniše u for petlji.
Naredni deo koda prikazije for petlju koja radi sa sekvencom tuple-ova i dodeljivanjem tuple-ova.

 

thetuple = [(10,3.14), (20, 14.5), (30, 17.9), (55, 2.31)]
thesum = 0
for (left,right) in thetuple:
   print "Reading ", left
   thesum += right
print "Average of lefts is ",thesum/len(thetuple)

 

Ovo daje sledeći rezultat:

 

Reading  10
Reading  20
Reading  30
Reading  55
Average of lefts is  9.4625

 

Sintaksa je malo drugačija od Java-e i C++. Navedeni primeri su jedini načini korišćenja for petlje u Python-u.

 

 

2.8.3. Ostali Python iteratori

for petlje u Python-u su veoma moćne izjave. One rade sa sekvencama, ali mogu se koristiti sa bilo kojim tipom podataka koji je iterabilan, tj. sa bilo kojim tipom podataka koji može generisati sekvencu vrednosti.

 

Zapis iterabilnog tipa podatka se zasniva na ideji da, ukoliko imaš jedan objekat određenog tipa podataka, možeš dobiti sledeći.  Za cele brojeve (integer) to je lako: ako imamo integer “x”, sledeća vrednost je “x+1”. Za listu, ukoliko si na petoj vrednosti liste, sledeća je šesta (list[5]). Python koristi ove ideje za sve iterabilne tipove podataka i određuje sintaksu za iteraciju.

 

Razmotrimo sledeći primer:

 

countries = ["France", "Germany", "United States", "Austria", "Norway"]
iterator = iter(countries)
print iterator.next()

 

Ovaj kod kreira iterator za listu countries i počinje da korača kroz vrednosti liste koristeći iterator. Rezultat print izjave je “France”. Sledeći poziv iter(countries) bi dao rezultat “Germany”.

 

Neki tipovi podataka imaju ugrađene iteratore. Na primer, fajlovi imaju iteratore:

 

ft = open("data.txt")
print ft.next()
print ft.next()
print ft.next()

 

Ovaj isečak ispisuje prva tri reda fajla “data.txt”.


Funkcija enumerate() je varijacija generisanja opsega i korisna je za iteraciju u for petljama.


Funkcija  enumerate() uzima listu i generiše iterator preko listi kao tuples-ova: prvi element svakog tuple-a je integer koji predstavlja poziciju drugog elementa u originalnoj listi. Razmotrimo sledeći primer:

 

countries = ["France", "Germany", "United States", "Austria", "Norway"]
for (offset, country) in enumerate(countries):
   print country, 'is the', offset, 'item in the list'

 

Opet, upotreba enumerate() funkcije proizvodi iterator, ne listu. Ovaj deo koda daje sledeći rezultat:

 

France is the 0 item in the list
Germany is the 1 item in the list
United States is the 2 item in the list
Austria is the 3 item in the list
Norway is the 4 item in the list

 

 

2.9. Funkcije

Funkcija je skup izjava, tretiran kao specijalna celina, koja se može koristiti više puta u programu. Korišćenje grupe izjava u funkciji je poznato kao pozivanje funkcije. Kada jedan deo programa pozove funkciju, on može proslediti podatke funkciji za rad funkcije; ti podaci su poznati kao parametri. Kao deo svog izvršavanja, funkcija može vratiti vrednost u pozivajuću izjavu (ali i ne mora).

 

Već smo videli kako rade funkcije. U prethodnim primerima, pozvali smo range() funkciju, prosledili joj integer parametar i koristili listu integer-a koju je vratila. U ovom delu, fokusiraćemo se na pisanje user-defined funkcija (definisanih od strane korisnika).

 

2.9.1. Pisanje User-Defined funkcija

Funkcije definisane od strane korisnika se pišu koristeći def izjave. Opšti oblik ove izjave se nalazi ispod:

 

def <function name>(<parameter list>):
   <statementset>

 

<function name> mora uzeti oblik svih ostalih imena u Python-u: mora počinjati sa slovom i može biti sačinjena od slova i brojeva. <parameter list> je opciona i to je lista imena koja će se koristiti za referenciranje parametara koji se šalju pri pozivanju funkcije. <statementset> je blok izjava, prikladno uvučenih (indentovanih), koje se izvršavaju kada se funkcija pozove.


Razmotrimo sledeći jednostavni primer:

 

def listaverage(aList):
   thesum = 0
   for x in aList:
      thesum += x
   print "Average is ",thesum/len(aList)

 

Ovo je prepakovana verzija prethodnog primera. Možemo pozvati ovu funkciju izvršavajući sledeću izjavu:

 

listaverage([1,2,3,4,5])

 

i dobiti ispis:

 

Average is  2

 

U definiciji, lista ‘[1,2,3,4,5]' biva dodeljena parametru aList i taj parametar se koristi kao promenljiva u definisanom kodu.

 

Definicije funkcija su ‘izvršive’ (eksekutabilne) i stvaraju se u toku izvršavanja programa (program runtime). Ovo se događa zbog interpretatorske prirode Python-a. To znači da def izjava može da se pojavi svuda gde se može pojaviti bilo koja druga izjava. Takođe možete tretirati ime funkcije kao promenljivu i dodeliti je drugim imenima. Razmotrimo sledeći primer:

 

if x < y:
   def announcement():
      print "x is indeed less than y!"
else:
   def announcement():
      print "Sorry, x is not less than y!"
 
shout = announcement
shout()

 

Rezultat ovog dela koda je, kad je x=1 i y=2:

 

x is indeed less than y!

 

 

2.9.2. Parametri funkcija

Parametri funkcija u Python-u su određeni kao lista nula ili više imenovanih parametara (ili „argumenata“) odvojenih zarezom. Kada se funkcija pozove, parametri mogu biti prosleđeni ili neimenovano u redosledu koji je definisan u funkciji, ili imenovano bilo kojim redosledom. U sledećem primeru, funkcija koja uzima tri parametra i dodaje ih se poziva koristeći i imenovane i neimenovane parametre:

 

def adder(a,b,c):
    return a+b+c
 

x=1
y=2
z=3
 
#odredi vrednosti parametra redom kojim je definisano u funkciji (neimenovani)
print adder(x,y,z)
 
#odredi vrednosti imenovanih parametara
print adder(b=x,c=y,z=a)

 

Možemo deklarisati default vrednosti za parametre u definiciji funkcije koristeći operator dodele. Kombinacija default vrednosti i imenovanih parametara daje nam dosta fleksibilnosti. U sledećem primeru podešavamo non-default parametre eksplicitno, bez potrebe da definišemo ostale parametre.

 

def adder(b,a=3,c=0):
    return a+b+c
 
x=1
print adder(b=x)

 

Navedeni primeri su odlični ukoliko želimo dodati nekoliko brojeva, ali šta u slučaju da želimo dodati proizvoljan broj brojeva? Jedan od načina bi bio da koristimo listu ili tuple kao argument. Drugi je da se upotrebi Python-ova specijalna sintaksa za određivanje promenljivog broj argumenata:

 

Ako su oba (i jedna i dupla zvezdica) argumenta definisana, dvostruka zvezdica mora biti definisana zadnja.

 

Sledeći primer pokazuje add funkciju koja koristi sintaksu jedne zvezdice da doda bilo koji broj neimenovanih parametara:

 

def add(firstValue=0,*myArgumentList):
    sum=firstValue
    for number in myArgumentList:
        sum+=number
        return sum
 
print add(1,2,3,4,5)

 

Python prosleđuje parametre u telo funkcije kao reference objekata. To znači da, ukoliko je objekat prosleđen kao parametar promenljivog tipa (poput liste, set-a ili dictionary-ja), promene na njemu unutar funkcije utiču na globalni referencirani objekat. Ukoliko je objekat nepromenljiv (poput tuple-a, string-a ili integer-a), ne može biti promenjen od strane funkcije. Ukoliko funkcija pokuša da promeni nepromenjiv tip objekta, Python kreira kopiju lokalnog opsega objekta za upotrebu unutar funkcije.

 

2.9.3. Povratne vrednosti

Python funkcije mogu vratiti vrednost(i) pozivatelju izvršavajući return izjavu, kao što je pokazano ispod. Ovaj princip vraćanja vrednosti se smatra boljim u odnosu na menjanje vrednosti promenljivih argumenata funkcije.

 

return <expressions>

 

<expressions> se izračunaju, izvršavanje funkcije se završi i vrednost se vraća pozivatelju. Ta vrednost se onda može koristiti kao bilo koja druga vrednost ili referenca promenljive. Ukoliko se nijedna vrednost ne vrati eksplicitno, funkcija vraća None.
Razmotrimo sledeći kod listaverege() funkcije:

 

def listaverage(aList):
   thesum = 0
   for x in aList:
      thesum += x
   return thesum/len(aList)

 

Sada poziv funkcije listaverege() ne ispisuje ništa, nego vraća prosek i može se koristiti u izrazu ili drugim izjavama.

 

 

Funkcije u Python-u mogu vraćati i višestruke vrednosti. Na primer:

 

def listaverage2(aList):
   thesum = 0
   for x in aList:
      thesum += x
   return len(aList),thesum/len(aList)

 

Ova funkcija vraća i dužinu aList-e i prosek. Možemo je koristiti ovako:

 

l,a = listaverage2(range(32))
print "l is ", l, " and a is ", a

 

i rezultat je:

 

l is  32  and a is  15

 

 

2.9.4. Pravila opsega važenja

Kao što smo videli, Python ima princip 'blokova izjava' – set izjava, grupisanih indentacijom. Identifikatorska imena za promenljive i druge objekte se mogu koristiti u ovim blokovima bez deklaracija. Blokovi mogu biti ugnježdeni jedni u druge i onda imena koja se koriste mogu postati zbunjujuća.

 

Koja je vrednost a koja se ispisuje u sledećem primeru?

 

a = 100
def f():
   a = 99
f()
print a

 

Python ima pravila opsega važenja koja definišu vidljivost identifikatora.

 

Da bismo razumeli ova pravila Python-a, moraju se razumeti neke definicije. Prvo moramo napraviti razliku između 'globalnog opsega' (global scope) i 'lokalnog opsega' (local scope). Globalni opseg je prostor identifikatora za ceo Python modul ili program. Svi blokovi na svim nivoima u programu mogu videti i koristiti globalna imena. Lokalni opseg je prostor za identifikatore u unutrašnjosti definicije funkcije. Između globalnog i lokalnog, postoji 'međuopseg' (enclosing scope). Ovo je prostor identifikatora definisanih vam prostora bloka izjave. I postoji još jedan opseg: svi ugrađeni Python identifikatori su dostupni svim delovima Python programa.

 

Sledeći grafik prikazuje odnose između opsega važenja (Grafik 1). Ovaj skup pravila se često naziva 'LEGB opsegom pravila'.

 

Grafik 1 . – Odnos između opsega važenja

 

opseg_vazenja_PY.png

 

 

Promotrimo LEGB pravilo pre nego što pređemo na primer.

  1. S obzirom da ne deklarišemo promenljive u Python-u, operacija koja ustanovljuje blok gde se ime definiše je 'dodeljivanje'.
  2. Kada se upotrebi ime, LEGB pravilo definiše sekvencu traženja koju Python koristi. Python počne da traži dodelu u lokalnom bloku, potom u međuprostornim blokovima, zatim u globalnim blokovima i na kraju u skupu ugrađenih Python definicija.
  3. Ime može biti deklarisano kao 'globalno', što ga stavlja u globalni opseg važenja.

Evo nekoliko primera:

 

a = 100
def f():
   a = 99
f()
print a

 

Ovim primerom je otvoreno ovo poglavlje. Koja će vrednost biti ispisana za a? Da bismo to otkrili, moramo znati na koje a se odnosi print izjava. Postoje dve a definicije, jedna u f() funkcijskom bloku i jedna u globalnom programskom bloku. Pravilo pretraživanja počinje u trenutnom bloku i kreće se 'šire', prema spoljnim blokovima, i stoga je a iz globalnog bloka, te ima vrednost 100. Primetite da ova funkcija menja sopstvenu vrednost a, pa ne utiče na globalno definisano a.

 

a = 100
def f():
   print a
f()
print a

 

U ovom primeru, dve vrednosti se ispisuju, obe se odnose na globalno ime a. Prva print izjava mora da nađe a koje će ispisati. Potraga počinje u lokalnom opsegu, ali nijedna dodela ne definiše a u tom opsegu. Stoga pretraga nastavlja u međuopsegu i tamo je a definisano sa vrednošću 100. Obe print izjave referenciraju ovu definiciju a.

 

a = 100
def f():
   print a
   a = a+100
   print a
f()
print a

 

Ovaj kod zapravo proizvodu grešku. Unutrašnjem opsegu funkcije treba definicija da ispiše a. Python interpreter traži definiciju u opsegu funkcije i nalazi izjavu dodele nakon print izjave. Ovo daje grešku jer je promenljiva referencirane pre nego što ima vrednost.

 

a = 100
def f():
   global a
   print a
   a = a+100
   print a
f()
print a

 

Ovaj kod je identičan prethodnom, ali sa dodatkom global deklaracije. Sada radi. Zbog global deklaracije, izjava dodele u funkciji koristi globalnu definiciju a umesto pokušaja da stvori novu.

 

2.9.5. lambda funkcije

 

Postoji način za definisanje funkcije u Python-u bez zadavanja imena toj funkciji kroz def izjavu. Ova vrsta anonimnih funkcija se definišu koristeći lambda izraz.

 

Lambda izraz da izrazite parametre i izraz koji definiše povratnu vrednost. Opšta forma izgleda ovako:

 

lambda <parameterlist>: <expression>

 

Na primer, mogli bi koristiti lambda funkciju ovako:

 

func = lambda a, b, c: (a + b + c) / 3
x = func(1,2,3)

 

U navedenom kodu, definišemo anonimnu funkciju koja vraća prosek tri broja. Ta definicija je dodeljena promenljivoj i ta promenljiva se koristi za pozivanje funkcije. Funkcija ni u jednom trenutku nije dobila ime, pa ako bi promenljiva func bila ponovo dodeljena, definicija ove funkcije bi bila izgubljena.


Moramo primetiti neka svojstva funkcije lambda. Prvo, upotreba lambda funkcije je izraz, ne izjava. To znači da definicija lambda funkcije vraća vrednost – funkcijsku vrednost – koja može biti dodeljenja promenljivoj. Drugo, telo lambda definicije je izraz, ne skup izjava. To znači da nema fleksibilnosti kao def definicija.
Međutim, lambda funkcije se mogu prikladno koristiti u nekoliko situacija. One su jednostavnije konstrukcije od def izjave. Jedna od čestih upotreba lambda funkcije jesta kao 'jump table'. Jump table je obično dictionary konstrukcija sa lambda funkcijama kao vrednostima.

 

Razmotrimo naredni primer:

 

table = {'green': (lambda: x * y),
      'orange': (lambda: x + y),
      'blue': (lambda: x - y + 2),
      'yellow': (lambda: x / y -1)
      }

 

Sada imamo tabelu koda. Pretpostavimo da imamo boju u promenljivoj color. Za referenciranje funkcije koja odgovara toj boji, mi bismo referencirali:

 

table[color]()

 

Ovo je mnogo jednostavnije od niza if izjava.

 

2.10. Klase i objekti

Pričali smo o raznim komponentama objektno-orijentisanog programiranja i sad je vreme da definišemo kako Python prihvata to.


Objektno-orijentisano programiranje sadrži nekoliko komponenti:

  1. Objekti: Objekat je središnji deo objektno-orijentisanog programiranja. Objekat obuhvata komponentu podatka sa operacijama nad tim podacima kao što je definisano u klasi.
  2. Klase: Klasa je template za objekat. Sastoji se od opisa operacija koje objekat može izvršiti i definicije strukture komponente podataka objekta (object's data component). Klase postoje u hijerarhijskoj strukturi kako bi se nasleđivanje (inheritance) bilo moguće. Ukoliko je jedna klasa podklasa druge, onda nasleđuje strukturu podataka i operacije od roditelja (parent).
  3. Metode: Metode su operacije koje se mogu izvršiti na komponenti podatka u objektu.

Objekat je kombinacija struktura podataka i metoda moje rade na toj strukturi podataka, po definiciji klase. Python dozvoljava da svaka od ovih komponenata bude definisana i korišćena. U Python-u, objekti koji se koriste za izvršavanje se nazivaju 'instance', a sazdane su od definicija klasa.

 

2.10.1. Definisanje klasa

Opšti oblik definisanja klase u Python-u izgleda ovako:

 

class <name>(<superclass>,...):
    <shared data section>
    <method section>

 

To izgleda jako zbijeno, evo primera:

 

class Account():
   def __init__(self, aStartingAmount):
      self.amount = aStartingAmount
 
   def deposit(self, aDepositAmount):
      self.amount += aDepositAmount
      return self.amount
 
   def withdraw(self, aWithdrawlAmount):
      self.amount -= aWithdrawlAmount
      return self.amount
 
   def balance(self):
      print self.amount

 

Ovo je klasa po imenu Account koja predstavlja bankovni račun. Ima data object nazvan amount koji se inicijalizuje sa vrednošću aStartingAmount u __init__() metodi. Kada se kreira instanca objekta, __init__() metoda – zvana 'konstruktor' – biva pozvana ukoliko postoji. Kreiranje instance mora odrediti vrednosti za parametre definisane konstruktorom.

 

Definicije metoda su samo definicije funkcija. Pošto smo već obradili funkcije, znamo i metode!

 

Svaki metod u prethodnoj definiciji ima bar jedan parametar. Ovaj parametar se odnosi na instancu klase koja je stvorena. Mi koristimo ovu instancu gore u referencama podataka. Kada koristimo self.amount, npr., pozivamo se na amount promenljivu koja postoji u određenoj instanci ove klase, pozvanu pomoću self.


Možemo koristiti ovu klasu na sledeći način:

 

bankaccount = Account(1000)
bankaccount.balance()
bankaccount.deposit(250)
bankaccount.withdraw(100)
bankaccount.balance()

 

Prva linija koda kreira 'instancirani objekat' po imenu bankaccount 'klase objekta' po imenu Account. Inicijalizujemo amount bankaccount objekta na 1000 uključujući ga i izjavu kreiranja instance. Ovo poziva konstruktor i prosleđuje mu vrednost 1000.
Ostatak primer koristi metode iz klase. Rezultat izvršavanja navedenog koda izgleda ovako:

 

1000
1150

 

 

2.10.2. Nasleđivanje

Nasleđivanje se događa kada se klase definišu po uslovima drugih klasa i preuzimaju njihove data components i metode. Ovo utvrđuje odnos između klasa koji se može koristiti za ponovnu upotrebu software-a. Nasleđivanje je jedno od najkorisnijih i najvažnijih koncepata objektno-orijentisanog programiranja i Python ga podržava u potpunosti.


Recimo da želimo da definišemo još dve bankarske klase: CheckingAccount i SavingsAccount. Mogli bismo proći kroz definisanje istih stvari kao u Account klasi i sve tri klase bi imale sopstvene definicije. Međutim, bili bismo u prednosti ukoliko bi ove klase bile u vezi: ukoliko bismo mogli koristiti jednu od ovih novih klasa na istom mestu kao Account. Zapravo, CheckingAccount je isti kao i Account, a SavingsAccount je Account sa specijalnom definicijom za withdraw koja ne dopušta da amount bude ispod $100.
Možemo definisati ove podklase ovako:

 

class CheckingAccount(Account):
   pass
 
class SavingsAccount(Account):
 
   def withdraw(self, aWithdrawalAmount):
      if (self.amount-aWithdrawalAmount > 100):
         self.amount -= aWithdrawalAmount
      return self.amount

 

U ovoj definiciji, CheckingAccount ima istu definiciju kao Account i stoga samo uključuje 'pass' izjavu (definicije klasa trebaju blok izjava). SavingsAccount je definisan u okvirima Account-a, ali redefiniše withdraw() metod.


Ovde, svaki put kada Account bude potreban, možemo koristiti CheckingAccount ili SavingsAccount, jer oni nasleđuju Account tip. Na primer, napišimo funkciju koja vrši transfer između dva računa:

 

def transfer(aAmount, aAcct1, aAcct2):
   aAcct2.deposit(aAcct1.withdraw(aAmount))

 

Ovaj kod bi radio jednako dobro sa AccountCheckingAccount ili SavingsAccount.

 

Često se hijerarhijska priroda nasleđivanja koristi da obezbedi programski interfejs 'očekivanih' implementacija metoda. Ovaj način obezbeđivanja interfejsa pretpostavlja da metod postoji ili obezbeđuje metod, no njegovu implementaciju ostavlja praznom.

 

Redefinišimo Account klasu:

 

class Account():
   def __init__(self, aStartingAmount):
      self.amount = aStartingAmount
 
   def deposit(self, aDepositAmount):
      self.amount += aDepositAmount
      return self.amount
 
   def withdraw(self, aWithdrawalAmount):
      if (self.amount-aWithdrawalAmount > self.limit):
         self.amount -= aWithdrawalAmount
      return self.amount
 
   def balance(self):
      print self.amount

 

Ovaj kod ima withdraw() metod zasnovan na promenljivoj po imenu self.limit, koja nije definisana Account klasom. Ovo čini Account klasu neupotrebljivom za regularno programiranje i pretvara je u 'apstraktnu klasu', onu koja se koristi kao bazna klasa za definisanje drugih klasa i da prisili te klase da imaju određenu definiciju. Sad, da bi bile upotrebljive, i CheckingAccount i SavingsAccount moraju definisati self.limit. Evo nove definicije ovih klasa:

 

class CheckingAccount(Account):
   def __init__(self, aStartingAmount):
      self.amount = aStartingAmount
      self.limit = 0
 
class SavingsAccount(Account):
   def __init__(self, aStartingAmount):
      Account.__init__(self,aStartingAmount)
      self.limit = 100

 

Ove definicije definišu self.limit promenljivu koja je pretpostavljena Account definicijom.
Primetite kako navedene definicije imaju dugačije načine za upotrebu roditeljske klase. CheckingAccount definicija prepisuje __init__() metod u potpunosti; SavingsAccount definicija poziva metod __init__() roditeljske klase pre prepravki. Drugi pristup je bolji jer ne pretpostavlja definiciju u roditeljskoj klasi. Ukoliko se __init__() metoda promeni u roditeljskoj klasi, SavingsAccount definicija će i dalje biti ispravna, dok definicija CheckingAccount neće.

 

 

2.10.3. Višestruko nasleđivanje

Trebali bismo ovde napomenuti Python-ovu mogućnost višestrukog nasleđivanja. Pokazali smo kako potklasa može naslediti osobine roditeljske klase. U Python-u, odnos nasleđivanja može se protezati na višestruke roditeljske klase.


Višestruko nasleđivanje je korisno kada klasa treba da pristupi data objects ili metodama većem broju klasa. Recimo da Python program ima set definicija koje definišu, između ostalog, klasu Zaposleni i klasu Student. Mogla bi postojati klasa koja definiše zaposlene studente, koja bi morala naslediti osobine obe prethodno pomenute klase.


Višestruko nasleđivanje se u Python-u definiše stavljanjem višestruke klase u class izjavu. Na primer, mogli bismo definisati:

 

class MenuItemWithButton(MenuItem, Button):
   ...

 

MenuItemWithButton klasa ima dva roditelja i mora implementirati ispravne apstrakne osobine iz oba.


Može se dogoditi da neke od klasa koje su nasleđenje imaju osobine sa istim imenom. U navedenom primeru, i MenuItem i Button klase bi mogle imati metod po imenu isPressed ili setVisible. Pravilo po kojem se odabire koje će referencirati samo jedno je „s leva na desno“: klase određene u class izjavi se pretražuju s leva na desno. Tako da, setVisible metod iz MenuItem-a bi bila korišćena metoda, osim ukoliko se redefiniše u novoj klasi.

 

2.11. Rad sa izuzecima

2.11.1. Osnove izuzetaka

Izuzeci su događaji koji definišu stanje koje je nastalo i koje treba rešiti. Ovi događaji su obično greške koje se dogode u toku izvršavanja programa, ali izuzeci se takođe mogu koristiti u drugim situacijama da se izmeni izvršavanje programa. Ključna osobina izuzetka je nužnost da on bude kontrolisan (handled). Nekontrolisani izuzeci zaustavljaju izvršavanje programa.


Python ima bogat skup jezičkih konstrukcija koje implementiraju izuzetke. Najosnovnija od ovih osobina je try/except sekvenca izjava. Opšti format je:

 

try:
   <statementset1>
except <name1>:
   <statementset2>
except <name2>,<data>
   <statementset3>
except (name3, name4):
   <statementset4>
except:
   <statementset5>
else:
   <statementset6>
finally:
   <statementset7>

 

Upotreba izuzetaka je veoma fleksibilna. Python započne pokušavajući (try) da izvrši <statementset1>. Ukoliko se ove izjave izvrše bez izuzetaka, izjave u opcionalnom else delu - <statementset6> - i finally deo - <statementset7> - se izvršavaju. Ako se pak dogodi izuzetak, pretražuju se except delovi u potrazi za imenom <name> izuzetka. Kada se nađe odgovarajući izuzetak, izvršavaju se izjave ispod tog except dela. Ukoliko se ime izuzetka ne nađe, izvršavaju se izjave ispod opšteg except dela <statementset5>. except izjava se može koristiti sa „<data>” elementom da bi se dobilo više informacija o izuzetku. finally deo se uvek izvršava.


Razmotrimo primer. Česta programerska greška je deljenje s nulom. S ovim je povezan izuzetak:

 

a = 10
b = 0
try:
   c = a/b
except:
   print "Oops...something bad happened"

 

Ovaj kod proizvodi poruku: „Oops...something bad happened“ i trebalo bi biti očigledno zašto je pokrenut izuzetak. U Python-u, ovaj izuzetak ima ime: 'ZeroDivisionError'. Možemo tražiti baš ovaj izuzetak upotrebom koda:

 

a = 10
b = 0
try:
   c = a/b
except ZeroDivisionError:
   print "Oops...something bad happened"

 

Ovaj kod daje isti rezultat. Dodatne informacije mogu biti pribavljene dodavanje promenljive except delu. U našem jednostavnom primeru, ova dodatna informacija je poruka o grešci koja objašnjava grešku:

 

a = 10
b = 0
try:
   c = a/b
except ZeroDivisionError, message:
   print "Oops...something bad happened"
   print message

 

Ovaj kod daje sledeći izlaz:

 

Oops...something bad happened
integer division or modulo by zero

 

Zaključimo ovaj jednostavan primer sa else izjavom.

 

a = 10
b = 10
try:
   c = a/b
except ZeroDivisionError, message:
   print "Oops...something bad happened"
   print message
else:
   print "Hey!  Something good happened"

 

Ovaj kod ispisuje poruku iz else izjave, jer je deljenje potpuno uspelo.


Zbog načina na koji je try izjava struktuirana, moguće je rukovati sa višestrukim izuzecima i mogu se implementirati uobičajeni načini za kompletiranje izjave. Sa višestrukim izuzecima se može rukovati na isti način, imenujući ih sve u except delu.


Postoje mnogi ugrađeni izuzeci u Python-u, previše da bismo ih sve nabrojali. Svi imaju imena i opisuju specifičnu vrstu greške. Python uvek ispiše ime izuzetka kada daje poruku o grešci. Stoga, ukoliko biste delili sa nulom u Python programu, dobili biste poruku o grešci nalik na ovu:

 

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

 

Ime izuzetka je u zadnjoj liniji.

 

 

2.11.2. Izazivanje (podizanje/rising) izuzetaka

Postoje mnogi ugrađeni izuzeci, ali možete pokretati i sopstvene izuzetke. Ovo je korisna osobina kada želite isforsirati da određena stanja (poput grešaka) budu obrađena od strane programera koristeći programerski interfejs. Izuzeci se izazivaju pomoću raise izjave, koja se može koristiti na nekoliko načina:

 

raise <exceptionname1>
raise <exceptionname2>,<value>

 

Prvi oblik je očigledniji: izaziva imenovani izuzetak. Ime može biti od ugrađenog izuzetka, string koji smo sami kreirali, ime klase ili instance.
Drugi oblik dodaje string koji služi kao dodatan podatak. String je obično poruka o grešci koja povećava izuzetak.


Uzmimo nekoliko primera. Razmotrimo dodatak definiciji klase Account:

 

BalanceError = 'BalanceError'
 
class Account():
   def __init__(self, aStartingAmount):
      self.amount = aStartingAmount
 
   def deposit(self, aDepositAmount):
      self.amount += aDepositAmount
      return self.amount
 
   def withdraw(self, aWithdrawalAmount):
      if (self.amount-aWithdrawalAmount > self.limit):
         self.amount -= aWithdrawalAmount
      else:
         msg = "Account is overdrawn by "+str(-(self.amount-aWithdrawalAmount))
         raise BalanceError,msg
      return self.amount
 
def balance(self):
   print self.amount
 
class CheckingAccount(Account):
   def __init__(self, aStartingAmount):
      self.amount = aStartingAmount
      self.limit = 0
 
class SavingsAccount(Account):
   def __init__(self, aStartingAmount):
      Account.__init__(self,aStartingAmount)
      self.limit = 100

 

Dodali smo nekoliko stvari. Sada imamo globalni string koji identifikuje 'BalanceError'. Ovo ćemo koristiti kao naš izuzetak. Takođe smo dodali 'else' deo withdraw() metodi koji kreira poruku o grešci u promenljivoj msg i izaziva BalanceError izuzetak. Sada možemo koristiti kod za hvatanje (catch) ovog novog izuzetka:

 

newacct = CheckingAccount(100)
try:
   newacct.withdraw(200)
except BalanceError,errormsg:
   print errormsg

 

 

2.11.3. Izuzeci i Propagiranje (širenje)

Izuzeci se negde moraju obraditi, u suprotnom program završava. Python 'propagira' izuzetke: prosleđuje ih kroz pozivajuću sekvencu i bilo koja funkcija u sekvenci ih može obraditi.


Na primer, ako funkcija 'funcA' pozove 'funcB', koja pozove 'funcC', imamo pozivajuću sekvencu koja je predstavljena na grafiku (Grafik 2. ):

 

Grafik 2 . – Pozivajuća sekvenca implicira propagiranje izuzetka

 

sekvenca_pozivanja_PY.png

 

 

Recimo da je izuzetak izazvan u 'funcC'. Ukoliko postoji 'rukovatelj (obrađivač) izuzecima' (exception handler) u 'funcC', Python koristi to. Ukoliko ne, prvo traži u 'funcB', i tako se penje kroz pozivajući lanac.

 

2.12. Import (Uvoz) Modula

Bilo bi nezgodno ako bi svaki Python program bio jedan fajl. Python programi zavise od mnogo ugrađenih definicija i od koda koji su drugi pisali. Stoga je smisleno podeliti definicije koda u višestruke fajlove. Tu na scenu stupa import-ovanje modula.


Imena Python programa završavaju sa '.py' sufiksom i nazivaju se 'moduli'. Program može referencirati stavke iz drugih modula uvozeći (import-ujući) ih. Sadržaji modula su poznati kao 'atributi' i mogu biti import-ovani kao velika jedinica ili individualno.


Evo primera. Pretpostavimo da su klase Account (čije su definicije iznad), child klase CheckingAccount i SavingsAccount i BalanceError izuzetak, sačuvane u fajlu nazvanom 'Account.py'. Ako kreiramo novi fajl sa instancom SavingsAccount klase, moramo importovati tu definiciju iz 'Account.py' ovako:

 

import Account
myaccount = Account.SavingsAccount(1000)
myaccount.balance()

 

Izjava import je načinila atribute definisane u 'Account.py' dostupnima trenutnom programu. Mi referenciramo atribute modula – u ovom slučaju, klase SavingsAccount – koristeći istu tačka notaciju kao i za referenciranje atributa klase. Možemo izbeći tačka notaciju i biti malo precizniji u import-ovanju koristeći ključnu reč 'from' uz import izjavu, kao u sledećem primeru:

 

from Account import SavingsAccount
myacct = SavingsAccount(1000)
myacct.withdraw(2000)

 

Međutim, izvršavanje poslednje linije izaziva izuzetak:

 

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "Account.py", line 17, in withdraw
raise BalanceError,msg
BalanceError: Account is overdrawn by 1000

 

Uvoz atributa modula može biti malo teži. Recimo da želimo da zarobimo izuzetak BalanceError u try izjavi, kao npr.:

 

from Account import SavingsAccount
try:
   myacct = SavingsAccount(1000)
   myacct.withdraw(2000)

except BalanceError, errormsg:
   print errormsg

 

I ovde dobijamo grešku:

 

Traceback (most recent call last):
   File "accttest.py", line 6, in <module>
      except BalanceError, errormsg:
NameError: name 'BalanceError' is not defined

 

To je zato što smo uvezli atribut SavingsAccount, ali ne i ostale. Referencirali smo ime BalanceError bez da smo ga prethodno uvezli. Možemo zaobići import-ovanje svakog atributa pojedinačno koristeći sledeću sintaksu za import izjavu:

 

from Account import *
try:
   myacct = SavingsAccount(1000)
   myacct.withdraw(2000)
except BalanceError, errormsg:
   print errormsg

 

Koristeći 'import', uvozimo svaki atribut i ne moramo koristiti tačka zapis da bismo ih referencirali. Ovo je najčešće korišćen oblik import izjave.


Možemo uvesti i ceo direktorijum fajlova i definicija. Ovo se zove uvoz paketa (package import). To je donekle napredna opcija Python-a, međutim, nju ovde ne opisujemo.

 

2.13. Razni elementi Python-a

S obzirom da završavamo ovaj kratak pregled Python-a, trebamo još spomenuti nekoliko raznovrsnih stavki.


Prva je docstrings. Docstrings je poseban oblik string-a koji se može koristiti u programskim fajlovima i definicijama funkcija. Oni se tretiraju kao komentari za vreme izvršavanja programa, te se ignorišu. Međutim, oni se koriste i prikazuju pomoću alata koji rade sa Python-om, uključujući Python runtime okruženje. Docstrings su okruženi sa tri apostrofa, kao što možete videti:

 

def sum(a,b,c):
   '''
   This is a contrived example to show
   off docstrings

   '''
   return(a+b+c)

 

Kada Python runtime sistem analizira docstring, skladišti ga kao __doc__ atribut stavke koja se definiše. Možemo štampati dokumentaciju koristeći sledeći kod:

 

>>> print sum.__doc__
 
   This is a contrived example to show
   off docstrings

 

Možemo štampati dokumentaciju poput ove i za ugrađene definicije. Na primer, funkcija max() je ugrađena u sistem Python runtime-a i možemo čitati dokumentaciju ovako:

 

>>> print max.__doc__
max(iterable[, key=func]) -> value
max(a, b, c, ...[, key=func]) -> value
 
With a single iterable argument, return its largest item.
With two or more arguments, return the largest argument.

 

Funkcija help() je ugrađena u Python runtime sistem i definisana je da ispisuje docstrings ako postoje.

 

Ovim dolazimo do drugog elementa Python-a koji spada u 'razne' elemente. Ugrađena dir() funkcija nam omogućava da istražujemo okruženje runtime-a i da utvrdimo definicije koje trenutno postoje. Ova funkcija ima dva oblika: bez parametara (ispisuje imena svih definicija); sa jednim parametrom (ispisuje atribute imena zadatog tim parametrom).


Na primer, ako želimo da vidimo sve definicije ugrađene u sum() funkciju koju smo prethodno opisali, pozivamo dir(sum). Dobijamo nešto nalik na:

 

>>> dir(sum)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

 

Obično su definicije koje započinju i završavaju sa „__“ karakterom sistemski definisane (definisane u sistemu). Sve ove definicije se mogu koristiti u Python kodu.

 

Kao što znamo, Python je dizajniran da sadrži jednu izjavu po liniji i da koristi indentacije (uvlačenja) za grupisanje izjava. To znači da ako napišemo izjavu koja zauzima više redova, Python bi mogao pogrešiti i gledati na dodatne linije kao na nove blokove koda i zaključiti da postoji greška u kodu. Za pisanje izjave koja se prostire preko više redova, koristimo „\“ karakter na kraju linije koja se nastavlja u novom redu.


Na primer, ovaj kod je sa greškom:

 

if (x < y and
   y > z and
   a > y):
   ...

 

Ali je ovaj ispravan:

 

if (x < y and \
   y > z and \
   a > y):
   ...

 

 

 

PyS60