Osnove programiranja - programski jezik Python
Sadržaj
Ovo su beleške sa vežbi.
- Beleške predstavljaju pojašnjenja i rešenja zadataka na predmetu Osnove Programiranja.
- Tekstove zadataka i materijale sa predavanja možete pronaći na enastav-i.
1 Upoznavanje sa python okruženjem
Kako da pokrenem Python?
- Otvorite terminal — može korišćenjem kombinacije tastera
Ctrl + Alt + t
a može i klikom na ikonicu. U terminalu kucate
python3
. Dobićete potpis Python-a (Python runtime-a koji trenutno radi na računaru).Python 3.4.3 (default, Sep 14 2016, 12:36:27) [GCC 4.8.4] on linux Type "help", "copyright", "credits" or "license" for more information. >>>
Ono što želite da kažete Python-u ide posle znaka
>>>
.- Na ovom kursu ćemo koristiti Python 3.
1.1 Komunikacija sa Python-om
U Python-u su 2 i 2 4.
2 + 2
4
Želimo da ispišemo nešto u konzolu?
print("Zdravo svima!")
Zdravo svima!
Želimo da napišemo funkciju?
# coding: utf-8 def say_hello(): print("Ćao!")
Želimo da pozovemo funkciju?
say_hello()
Ćao!
Napuštanje Python interpretera u terminalu
exit()
1.2 Pokratanje Python programa
Kada želimo da pokrenemo program koji se nalazi na putanji /home/vas_username/Desktop/ime_fajla.py
:
python3 /home/vas_username/Desktop/ime_fajla.py
1.3 Terminal
- Pri otvaranju terminala uvek se nalazimo u home direktorijumu korisnika koji je ulogovan na sistem.
Želim da vidim gde se trenutno nalazim
pwd
Želimo da idemo u neki direktorijum
cd putanja/do/vaseg/direktorijuma
Vraćanje u home direktorijum.
cd ~
Šta imam u direktorijumu?
ls
Želim da znam koje postoje opcije neke komande
man naziv_komande # man ls
1.4 primer chaos.py
Imamo primer jednog Python programa:
# File: chaos.py # A simple program illustrating chaotic behavior. def main(): print("This program illustrates a chaotic function") x = eval(str(input("Enter a number between 0 and 1: "))) for i in range(10): x = 3.9 * x * (1 - x) print(x) main()
Neki standardni idiomi petlji:
# nije mi bitno koliko puta se izvršava petlja for _ in range(3): print("Hello!") # ista stvar samo sa while i = 0 while i < 3: i += 1 print("Hello!")
... ... Hello! Hello! Hello! ... >>> ... ... ... Hello! Hello! Hello!
Koji je čitljiviji način?
Promeniti ispis
chaos.py
tako da ispisuje u tabeli sa 2 kolone.def chaos(): print("This program illustrates a chaotic function") first = eval(str(input("Enter first number between 0 and 1: "))) second = eval(str(input("Enter second second between 0 and 1: "))) for i in range(10): x = 3.9 * first * (1 - first) y = 3.9 * second * (1 - second) print(str(x) + 4 * " " + "|" + 4 * " " + str(y))
# za 0.2 i 0.3 chaos()
This program illustrates a chaotic function 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819 0.6240000000000001 | 0.819
2 Python programi
2.1 convert.py
Program sa uvodnom porukom:
# convert.py # A program to convert Celsius temps to Fahrenheit # by: Susan Computewell def fahrenheit(): print("Dobrodošli u program koji konvertuje Farenhajte u Celzijuse") celsius = eval(str(input("What is the Celsius temperature? "))) fahrenheit = 9/5 * celsius + 32 print("The temperature is " + str(fahrenheit) + " degrees Fahrenheit.")
Pozivanje funkcije:
fahrenheit()
Dobrodošli u program koji konvertuje Farenhajte u Celzijuse What is the Celsius temperature? 12 The temperature is 53.6 degrees Fahrenheit.
convert.py
takav da ispisuje konverziju u tabeli od 10 do 100 stepeni Celzijusa.
def fahrenheit_table(): print("celzius" + "\t" + "|" + "\t" + "farenhajt") print(30 * "-") for c in range(0, 100, 10): f = 9/5 * c + 32 print(str(c) + "\t" + "|" + "\t" + str(f))
Poziv:
fahrenheit_table()
celzius | farenhajt ------------------------------ 0 | 32.0 10 | 50.0 20 | 68.0 30 | 86.0 40 | 104.0 50 | 122.0 60 | 140.0 70 | 158.0 80 | 176.0 90 | 194.0
2.2 Eval funkcija Python interpretera
eval(...)
je funkcija kojom praktično kažete:
"Python-e, ovaj tekst koji ti dajem izvrši kao program."
broj = eval('10') broj n_torka = eval('1, 2, 3') n_torka lista = eval('[11, 22, 33]') lista
10 >>> (1, 2, 3) >>> [11, 22, 33]
2.3 avg.py
Program koji izračunava srednje vrednosti:
# avg2.py # A simple program to average two exam scores # Illustrates use of multiple input def my_avg(): print("This program computes the average of two exam scores.") score1, score2, score3 = eval( str(input("Enter three scores separated by a comma: "))) average = (score1 + score2 + score3) / 3 print("The average of the scores is: " + str(average))
Poziv funkcije:
my_avg()
This program computes the average of two exam scores. The average of the scores is: 11
2.4 futval.py
Program za računanje investicija
# futval.py # A program to compute the value of an investment # carried 10 years into the future def futval(): print("This program calculates the future value") print("of a 10-year investment.") principal = eval(input("Enter the initial principal: ")) inflation = eval(input("Enter actual inflation: ")) for i in range(10): principal = principal/(1 + inflation) print("The value in 10 years is: " + str(principal)) futval()
... ... >>> ... ... ... ... ... ... ... ... >>> This program calculates the future value of a 10-year investment. The value in 10 years is: 7.253815028640569
3 Brojevi
3.1 Različito
4 != 2 ** 2
False
3.2 Math lib
Sva naprednija matematika se nalazi u biblioteci math
. Da bismo je koristili
moramo je importovati.
- Trigonometrija radi u Radijanima
import math
math.sin(90)
math.cos(90)
math.sqrt(4)
>>> 0.8939966636005579 -0.4480736161291701 2.0
3.3 range funkcija
- Vraća lenju sekvencu - objekat
range
čiji se pojedinačni elementi izračunavaju naknadno po potrebi. O lenjoj evaluaciji možete čitati ovde.
for i in range(3): i
... 0 1 2
for i in range(10, 20, 2): i
... 10 12 14 16 18
for i in range(10, -10, -3): i
... 10 7 4 1 -2 -5 -8
3.4 Prosta primena brojeva
Program koji izračunava cenu pice po kvadratnom centimetru za dati poluprečnik i cenu cele pice:
# import alias import math as m def cena_po_centimetru(r, cena): """Izracunava cenu pice po kvadratnom centimetru. Arguments: r -- poluprecnik cele pice u centimetrima cena -- cena cele pice u dinarima""" # kovertujem argumente iz stringa u realne brojeve r = float(r) cena = float(cena) print("Cena pice po kvadratnom centimetru je: " + str(r ** 2 * m.pi / cena) + " din.")
Obratite pažnju na docstring kojim opisujemo šta radi funkcija. Više o tome možete naći ovde.
Pozivanje funkcije:
r = eval(input("Unesite poluprecnik pice u centimetrima: ")) cena = eval(input("Unesite cenu cele pice u dinarima: ")) cena_po_centimetru(r, cena)
... >>> Cena pice po kvadratnom centimetru je: 12.723450247 din.
3.5 Izračunavanje molekularne mase ugljovodonika:
def carbohydrate_mm(carbo, hydro): m_C = 12.011 m_H = 1.0079 return "Molekularna masa takvog jedinjenja je: " \ + str(carbo * m_C + hydro * m_H) + "." br_C = eval(input("Unesite broj ugljenika: ")) br_H = eval(input("Unesite broj vodonika: ")) print(carbohydrate_mm(br_C, br_H))
... ... ... >>> >>> >>> >>> Molekularna masa takvog jedinjenja je: 178.4006.
3.6 Udaljenost munje
def flash_distance(happened, heard): sound_speed = 340 distance = (heard - happened) * sound_speed is_dead = True if distance == 0 else False output = "Udaljenost posmatraca od munje je: " + str(distance) # ako je udaljenost munje od posmatraca 0, pretpostavimo da se posmatrac # nije bas proveo najbolje if is_dead: return output + "\n I tu je kraj." else: return output munja_se_desila = eval(input("Unesite kada se desila munja: ")) covek_je_cuo_munju = eval(input("Unesite kada je covek cuo munju: ")) print(flash_distance(munja_se_desila, covek_je_cuo_munju))
... ... ... ... ... ... ... ... ... ... >>> >>> >>> >>> Udaljenost posmatraca od munje je: 37740
Poziv funkcije koji nije dobar za posmatrača…
print(flash_distance(0, 0))
Udaljenost posmatraca od munje je: 0 I tu je kraj.
3.7 Zbir prvih n
prirodnih brojeva
- Način koji troši vreme.
sum
realizuje sve brojeve izrange
kako bi ih sabrala. To troši vreme.
unos = eval(input("Unesite n: ")) print(sum(range(unos + 1)))
>>> 10
Bolji način. Kolikigod broj da prosledimo funkciji
first_n
ona će se izvršiti momentalno, nema nikakakvog prolaženja - iteracije.def first_n(n): return int((n * (n + 1))/2) print("Po formuli: " + str(first_n(10000000)))
... >>> Po formuli: 50000005000000
3.8 Unos sa tastature n
brojeva i njihov zbir
- Način sa
for
:
broj_brojeva = eval(input("Unesite broj brojeva koje zelite da unesete: ")) suma = 0 for b in range(broj_brojeva): suma += eval(input("Unesite " + str(b + 1) + ". broj: ")) print("Zbir je: " + str(suma))
Unesite broj brojeva koje zelite da unesete: 5 Unesite 1. broj: 12 Unesite 2. broj: 34 Unesite 3. broj: 123 Unesite 4. broj: 12 Unesite 5. broj: 45 Zbir je: 226
Način sa evaluacijom u tuple:
def n_inputs(): br_brojeva = eval(input("Unesite broj brojeva koje želite da sabirate: ")) unos = eval(input("Unesite te brojeve razdvojene zarezon: ")) if len(unos) != br_brojeva: print("Uneli ste " + str(len(unos)) + " brojeva umesto " + \ str(br_brojeva) + " koliko ste rekli da cete uneti.") else: print("Zbir brojeva je: " + str(sum(unos))) n_inputs()
Unesite broj brojeva koje želite da sabirate: 3 Unesite te brojeve razdvojene zarezon: 3424,321421,3213 Zbir brojeva je: 328058 ---------------------------------------- Unesite broj brojeva koje želite da sabirate: 4 Unesite te brojeve razdvojene zarezon: 1,2,34,565,67 Uneli ste 5 brojeva umesto 4 koliko ste rekli da cete uneti.
4 Rukovanje stringovima
Stringovi i liste se u Pythonu predstavljaju na sličan način, često ono što važi za liste važi i za Stringove.
Napraviti novi string od prva 3 karaktera jednog i poslednja 2 karaktera drugog.
def build_from_begin_end(first_s, second_s): return first_s[:3] + second_s[-3:] f = input("Unesite prvi strng: ") s = input("Unesite drugi strng: ") print(build_from_begin_end(f, s))
Napraviti akronim
def acronim(text): acronim = "" for word in text.split(" "): acronim += word[0].upper() return acronim text = input("Unesite tekst: ") print(acronim(text))
5 Fajlovi
Upisati korisničke kredencijale u fajl
Korišćeni koncepti:
with
izraz. Služi nam ovde kako ne bismo morali eksplicitno da pišemofajl.close()
. Ova konstrukcija ume sama da otpusti resurs kada je to potrebno — kada sewith
blok završi.try...except
konstrukcija.
Znači sledeće: pokušaj da uradiš sve što je navedeno utry
bloku, ako se pri tome desi neka greška tu stani i uradi ono što je uexcept
bloku.
Ovde je korišćena kako bismo ispisali na konzolu da je došlo do greške pri čitanju fajla, ako se to desi.
# coding: utf-8 import os def save_credentials(uname, passw): try: with open("credentials.supa_od_kornjače", 'a') as creds: creds.write(uname + "|$# ovo je delimiter #$|" + passw + os.linesep) print("Uspesno ste upisali u fajl vaše kredencijale, %s." % uname) except: print("Doslo je do greske pri upisu.") uname = input("Unesite vas username: ") passwd = input("Unesite vas password: ") save_credentials(uname, passwd)
>>> >>> >>> ... ... ... ... ... ... ... >>> Uspesno ste upisali u fajl vaše kredencijale, Petar.
[Obratiti pažnju!]
Fajlovi se u fajl sistemu identifikuju preko URI-ja tj. lokacija u fajl sistemu.
U kontekstu fajl sistem ime fajla kao pojam NE POSTOJI, postoji samo njegova putanja čiji kraj mi (ljudi) između sebe zovemo "ime fajla".
Kada navedemo samo ime fajla (kao u primeru iznadcredentials.supa_od_kornjače
) onda zapravo upućujemo fajl sistem da traži u repozitorijumu u kojem se trenutno izvršava program. Dakle:ime.extension == putanja/do/direktorijuma/gde/se/izvrsava/program/ime.extension
putanja/do/direktorijuma/gde/se/izvrsava/program/ se još zove i current directory i u terminalu u kome pokrećete program se može dobiti sa:
pwd
Čitanje podataka iz fajla
# coding: utf-8 def read_credentials(file_name): delimiter = "|$# ovo je delimiter #$|" with open(file_name, 'r') as f: for l in f.readlines(): print("korisničko ime: " + l.split(delimiter)[0]) print("lozinka: " + l.split(delimiter)[1]) read_credentials("credentials.supa_od_kornjače")
>>> ... ... ... ... ... ... >>> korisničko ime: sda lozinka: dsad korisničko ime: pera lozinka: petrovicNjegos
5.1 Program koji kombinuje sadržaj dva fajla i snima ga u treći
import os def kombinuj(korisnci_f, artikli_f , statistika_f): delimiter = "|" with open(korisnci_f, 'r') as korisnici, \ open(artikli_f, 'r') as artikli, \ open(statistika_f, 'w') as statistika: korisnici_lines = korisnici.readlines() artikli_lines = artikli.readlines() # koliko ima korisnika toliko ima i evidencija o njihovim artiklima l = len(korisnici_lines) for i in range(l): ime = korisnici_lines[i].split(delimiter)[0] # nalazim ukupnu cenu artikala iz odgovarajuce linije cene_kao_stringovi = artikli_lines[i].split(delimiter) ukupna_cena = 0 for c in cene_kao_stringovi: ukupna_cena += int(c) # racunam prosecnu cenu prosecna_cena = ukupna_cena / len(cene_kao_stringovi) # upisujem u statistiku u fajl statistika.write(ime + delimiter + str(ukupna_cena) + delimiter + str(prosecna_cena) + os.linesep) kombinuj("korisnici.txt", "racuni.txt", "statistika.txt")
5.1.1 Isti program, malo pametnije
Korišćeni koncepti:
zip
funkcija. Ovde nam služi da istovremeno iteriramo kroz linije u oba fajla. Veoma je korisna i često se koristi u Python-u.
U Python 3zip
funkcija vraća lenji objekat (slično kaorange
, to je razmatrano ovde):# U python 3 zip([1, 2, 3], ["zec", "macke", "psa"], ["skace", "predu", "laju"])
<zip object at 0x7f6de7201308>
# realizujem zip-ovanu listu tako što joj pristupam u for-u for (broj, zivotinja, predikat) in zip([1, 2, 3], \ ["zec", "macke", "psa"], \ ["skace", "predu", "laju"]): print(broj, zivotinja, predikat)
... ... ... ... 1 zec skace 2 macke predu 3 psa laju
U Python 2
zip
funkcija vraća već izvršenu(evaluiranu) listu (pa je tu jasnije šta zaista radizip
):# U python 2 zip([1,2,3], ["zec", "macke", "psa"], ["skace", "predu", "laju"])
[(1, 'zec', 'skace'), (2, 'macke', 'predu'), (3, 'psa', 'laju')]
- List comprehensions da pretvorimo
string
-ove uint
-ove kako bismo mogli da ih saberemo sasum
.
List comprehensions je veoma moćan i često korišćen koncept u Python-u.
Za Python važi - tamo gde može list comprehensions nikada ne koristiti imperativne for petlje.
import os def kombinuj(korisnci_f, artikli_f , statistika_f): delimiter = "|" with open(korisnci_f, 'r') as korisnici, \ open(artikli_f, 'r') as artikli, \ open(statistika_f, 'w') as statistika: for (l_korisnik, l_artikli) in zip(korisnici.readlines(), artikli.readlines()): ime = l_korisnik.split(delimiter)[0] # ova konstrukcija se zove list comprehensions cene = [int(c) for c in l_artikli.split(delimiter)] # nalazim ukupnu cenu artikala iz odgovarajuce linije ukupna_cena = sum(cene) # racunam prosecnu cenu prosecna_cena = ukupna_cena / len(cene) # upisujem statistiku u fajl # prikaz na tri decimale statistika.write(ime + delimiter + '%.3f'%ukupna_cena + delimiter + '%.3f'%prosecna_cena + os.linesep) kombinuj("korisnici.txt", "racuni.txt", "statistika2.txt")
5.1.2 Dodatak o try...except
konstrukciji za_radoznale
Želimo da otvorimo fajl sa putanje. Putanja se prosleđuje kao parametar funkcije tako da korisnik može proslediti neku nepostojeću putanju. Ako se to desi potrebno je obavestiti korisnika da fajl na putanji ne postoji i otvoriti podrazumevani fajl.
def otvaranje_fajla(file_name): try: # pokušavam da otvorim fajl čije je ime prosleđeno kao argument with open(file_name, 'r') as f: return f.readlines() except FileNotFoundError: # ako je Python "bacio" FileNotFoundError onda uradi ovo ovde print("""Fajl koji želite da otvorite ne postoji. Otvoriću podrazumevani...""") with open("korisnici.txt", 'r') as f: return f.readlines()
# ova putanje ne postoji otvaranje_fajla("C:\\Documents\Pictures\more.jpg")
Fajl koji želite da otvorite ne postoji. Otvoriću podrazumevani... ['pera|peric\n', 'jova|jovic\n', 'steva|stevic\n']
except
blok koji nema navedenu klasu greške će uhvatiti sve greškedef otvaranje_fajla(file_name): try: # pokušavam da otvorim fajl čije je ime prosleđeno kao argument with open(file_name, 'r') as f: return f.readlines() except: # ako je Python "bacio" bilo kakvu grešku print("""Fajl koji želite da otvorite ne postoji. Otvoriću podrazumevani...""") with open("korisnici.txt", 'r') as f: return f.readlines()
Ovaj program je ekvivalentan sa onim predhodnim.
U
except
izrazu je takođe mogućas
izraz:def otvaranje_fajla(file_name): try: # pokušavam da otvorim fajl čije je ime prosleđeno kao argument with open(file_name, 'r') as f: return f.readlines() except FileNotFoundError as e: # ako je Python "bacio" FileNotFoundError onda uradi ovo ovde print("""Fajl koji želite da otvorite ne postoji. Otvoriću podrazumevani...""") print("Greška koju ste dobili je: " + e.strerror) with open("korisnici.txt", 'r') as f: return f.readlines()
otvaranje_fajla("C:\\Documents\Pictures\more.jpg")
Fajl koji želite da otvorite ne postoji. Otvoriću podrazumevani... Greška koju ste dobili je: No such file or directory ['pera|peric\n', 'jova|jovic\n', 'steva|stevic\n']
Moguće je navoditi više
except
blokova, tada će biti uhvaćena prva greška koja odgovara grešci koja se desila.def otvaranje_fajla(file_name): try: # pokušavam da otvorim fajl čije je ime prosleđeno kao argument with open(file_name, 'r') as f: return f.readlines() except FileNotFoundError as e: # ako je Python "bacio" FileNotFoundError onda uradi ovo ovde print("""Fajl koji želite da otvorite ne postoji. Otvoriću podrazumevani...""") print("Greška koju ste dobili je: " + e.strerror) with open("korisnici.txt", 'r') as f: return f.readlines() except IOError: print("Desio se neki error")
otvaranje_fajla("nepostojeća putanja")
Fajl koji želite da otvorite ne postoji. Otvoriću podrazumevani... Greška koju ste dobili je: No such file or directory ['pera|peric\n', 'jova|jovic\n', 'steva|stevic\n']
# coding: utf-8 import os def otvaranje_fajla(file_name): try: # pokušavam da otvorim fajl čije je ime prosleđeno kao argument with open(file_name, 'r') as f: return f.readlines() except IOError as e: # iako se zapravo desio FileNotFoundError ipak će se ući u ovaj blok # jer je FileNotFoundError vrsta IOError-a a # ovaj except blok je naveden pre onog sa FileNotFoundError-om print("Desio se neki error sa porukom: " + e.strerror + os.linesep + "i brojem: " + str(e.errno)) except FileNotFoundError as e: print("""Fajl koji želite da otvorite ne postoji. Otvoriću podrazumevani...""") print("Greška koju ste dobili je: " + e.strerror) with open("korisnici.txt", 'r') as f: return f.readlines()
otvaranje_fajla("nepostojeća putanja")
Desio se neki error sa porukom: No such file or directory i brojem: 2
6 Funkcije
Funkcije su izrazi (expressions) koji enkapsuliraju radnju nad nekim podacima.
Zato svaka funkcija treba da ima:
- ime (ime funkcije se zadaje malim slovima tako da se reči odvajaju donjom crtom
_
— standardi pisanja Python koda su dati ovde). - dokumentaciju (tekst koji opisuje šta ta funkcija radi, o tome je bilo reči ranije)
- listu parametara (podatci nad kojima se vrši radnja — idealno bi bilo da se radnja vrši samo nad njima)
- telo (opis same radnje)
povratnu vrednost (rezultat koji ta radnja ima — idealno bi bilo da on zavisi samo od parametara)
# coding: utf-8 def ime_funkcije(parametar_1, parametar_2): """Ova funkcija sabira dva broja. Ovde ide širi opis toga što radi funkcija. Ova prosto sabira dva broja. Args: parametar_1: Prvi broj koji se sabira. parametar_2: Drugi broj koji se sabira. Returns: Broj koji je rezultat sabiranja Raises: ... """ # telo ret_val = parametar_1 + parametar_2 # povratna vrednost return ret_val
Nakon definicije funkcije se mogu pozvati.
Funkcija se poziva tako što se navede njeno ime sa sintaksom poziva (to praktično znači ime_funkcije()
, ()
je sintaksa poziva u Python-u).
# 2 i 3 su argumenti ime_funkcije(2, 3)
5
6.1 Čiste funkcije
Ako Python izraze želimo da posmatramo kao matematičke izraze onda moramo obezbediti da važi matematički princip zamene simbola.
Šta to praktično znači?
Ovako izgleda niz nekih matematičkih izraza:
x = 1 y = 2 def f1(x): return 5 * x + 1 def f2(x): return x**2 + 2 # Koliko je z? z = f1(x) + f2(y)
Ovo se rešava tako što prosto zamenjujemo simbole:
z = f1(1) + f2(2)
Dalje imamo:
z = (5 * 1 + 1) + (2**2 + 2)
Pa je rezultat:
z
12
Da bismo Python izraze posmatrali kao matematičke izraze moramo obezbediti da sve funkcije budu čiste i bez sporednih efekata.
Funkcije su čiste (pure functions) ako za njih važe oba naredna uslova?
- Uvek daju isti rezultat za iste argumente.
- Nemaju sporednih efekata (side effects)
Šta znači da funkcije nemaju sporednih efekata?
- Ne menjaju globalne variable niti bilo koje druge koje nisu u
njenom vidljivom opsegu (tj. nisu definisane unutar njenog
def
bloka, u Python-u)
Ako su naše funkcije čiste onda UVEK možemo da kažemo šta će one
uraditi u našem sistemu.
Tako što prosto zamenimo pozive funkcija sa njihovim rezultatom.
Kada izraz poziva funkcije možemo uvek da zamenimo njenim rezultatom onda za funkciju kažemo da je referencijalno transparentna.
6.2 Primeri funkcija
Funkcija koja učitava fajl i njegova sadržaj vraća kao listu.
# coding: utf-8 def citanje_iz_fajla(file_name, delimiter): """Učitava kredencijale korisnika. Args: file_name: Ime fajla u kome se nalaze kredencijali. delimiter: Delimiter koji je korišćen u fajlu. Returns: Vraća listu koja sadrži parove (kao liste) korisničkog imena i lozinke. """ with open(file_name, 'r') as f: return [l.strip().split(delimiter) for l in f.readlines()]
print(citanje_iz_fajla ("credentials.supa_od_kornjače", "|$# ovo je delimiter #$|"))
[['uno', 'dos'], ['kilo', 'kilo'], ['joki', 'kijo'], ['mile', 'milic']]
Funkcija koja vrši registraciju korisnika
# coding: utf-8 import os def upisi_fajl(uname, pas, file_name, delimiter): """Vrši registraciju korisnika tako što upisuje njihove kredencijale u fajl. Args: uname: Korisnično ime pas: Lozinka file_name: Ime fajla u koji se upisuju kredencijali korisnika delimiter: Delimiter koji je korišćen u tom fajlu Retruns: Vraća listu koja sadrži parove (kao liste) korisničkog imena i lozinke. """ with open(file_name, 'a') as f: f.write(uname + delimiter + pas + os.linesep) with open(file_name, 'r') as f: return [l.strip().split(delimiter) for l in f.readlines()]
upisi_fajl("ana", "ananananic", "credentials.supa_od_kornjače", "|$# ovo je delimiter #$|")
[['uno', 'dos'], ['kilo', 'kilo'], ['joki', 'kijo'], ['mile', 'milic'], ['ana', 'ananananic']]
7 Grananje — upravljanje tokom programa
Kontrola toka programa ili grananje se odnosi na mehanizam kojim usmeravamo tok izvršavanja programa u odnosu na postavljene uslove.
Osnovna konstrukcija grananja u Python-u je if ... [elif]* ... else
:
if uslov: posao_ako_je_uslov_ispunjen else: posao_ako_uslov_nije_ispunjen
Uslova u opštem slučaju može biti više:
if uslov_1: posao_ako_je_uslov_1_ispunjen elif uslov_2: posao_ako_je_uslov_1_neispunjen_a_uslov_2_ispunjen ... elif uslov_n: posao_ako_su_uslov_1_do_uslov_n-1_neispunjeni_a_uslov_n_ispunjen else: posao_ako_ni_jedan_navedeni_uslov_nije_ispunjen
Program koji uzračunava zaradu radnika u odnosu na radne sate.
# coding: utf-8 import os def racunanje_zarada(ime_f, cena_sata): koeficijent_uvecanja = 1.5 with open(ime_f, 'r') as f: for l in f.readlines(): ime_radnika = l.split('|')[0] radni_sati = l.split('|')[1:] # konvertujem tekst u brojeve zbir = 0 for sat in radni_sati: zbir += int(sat) osnovna_plata = cena_sata * zbir # provera da li je broj radnih sati veći od 40 # ovo je grananje if zbir > 40: print("ime: " + ime_radnika + os.linesep + "zarada: " + str(osnovna_plata * koeficijent_uvecanja)) else: print("ime: " + ime_radnika + os.linesep + "zarada: " + str(osnovna_plata))
racunanje_zarada("radnici.txt", 1000)
ime: pera zarada: 61500.0 ime: jova zarada: 38000 ime: steva zarada: 40000
Isti zadatak ali uz malo pametnije računanje zbira radnih sati. Korišćen je koncept list comprehensions.
# coding: utf-8 import os def racunanje_zarada(ime_f, cena_sata): koeficijent_uvecanja = 1.5 with open(ime_f, 'r') as f: for l in f.readlines(): ime_radnika = l.split('|')[0] radni_sati = l.split('|')[1:] zbir = sum([int(s) for s in radni_sati]) osnovna_plata = cena_sata * zbir # provera da li je broj radnih sati veći od 40 # ovo je grananje if zbir > 40: print("ime: " + ime_radnika + os.linesep + "zarada: " + str(osnovna_plata * koeficijent_uvecanja)) else: print("ime: " + ime_radnika + os.linesep + "zarada: " + str(osnovna_plata))
racunanje_zarada("radnici.txt", 1000)
ime: pera zarada: 61500.0 ime: jova zarada: 38000 ime: steva zarada: 40000
Program koji ocenjuje učenike po tabeli ocena.
def ocenjivanje(bodovi): if bodovi > 0 and bodovi < 55: return 5 elif bodovi >= 55 and bodovi < 65: return 6 elif bodovi >= 65 and bodovi < 75: return 7 elif bodovi >= 75 and bodovi < 85: return 8 elif bodovi >= 85 and bodovi < 95: return 9 elif bodovi >= 95 and bodovi <= 100: return 10 else: return "Nepoznata ocena"
ocenjivanje(77) ocenjivanje(95) ocenjivanje(96) ocenjivanje(56) ocenjivanje(101)
8 10 10 6 'Nepoznata ocena'
Program koji računa body mass index i određuje stepen uhranjenosti po tabeli.
# coding: utf-8 def indeks_telesne_mase(masa, visina): bmi = masa / visina**2 if bmi <= 18.5: return "Pothranjenost" elif bmi > 18.5 and bmi <= 25: return "Idealna telesna težina" elif bmi > 25 and bmi <= 30: return "Preterana telesna težina" elif bmi > 30: return "Gojaznost"
print(indeks_telesne_mase(55,1.8)) print(indeks_telesne_mase(75,1.8)) print(indeks_telesne_mase(82,1.8)) print(indeks_telesne_mase(120,1.8))
Pothranjenost Idealna telesna težina Preterana telesna težina Gojaznost
Program koji računa kaznu za prekoračenje brzine.
# coding: utf-8 def kazna(brzina, ogranicenje): glavnica = 5000 dodatak_po_jedinici_prekoracenja = 500 dodatak_za_iznad_120 = 5000 prekoracenje = brzina - ogranicenje za_uplatu = 0 # ako je prekoracio naplati mu za svaku jedinicu prekoracenja jos # dodatnih 500 dinara if brzina < 120 and prekoracenje > 0: za_uplatu = glavnica za_uplatu += prekoracenje * dodatak_po_jedinici_prekoracenja # ako je brzina veća od 120 km/h naplati 5000 dinara za svaku jedinicu # prekoračenja if brzina > 120: prekoracenje_preko_120 = brzina - 120 za_uplatu += prekoracenje_preko_120 * dodatak_za_iznad_120 if za_uplatu > 0: return "Vaša kazna iznosi: " + str(za_uplatu) + "." else: return "Niste prekoračili brzinu."
print(kazna(80,60)) print(kazna(50,60)) print(kazna(130,60))
Vaša kazna iznosi: 15000. Niste prekoračili brzinu. Vaša kazna iznosi: 50000.
Program koji računa zaradu dadilje.
Korišćeni koncepti:
- funkcija
round(...)
. Zaokružuje razlomljen broj na ceo.
def vreme_u_minute(vreme): """Pretvara vreme u formatu hh:mm u minute.""" h, m = [int(i) for i in vreme.split(":")] return h*60 + m def minuti_u_sate(minuti): """Pretvara minute u sate.""" return minuti / 60 def dadilja(pocetak, kraj): p = vreme_u_minute(pocetak) k = vreme_u_minute(kraj) devet = vreme_u_minute("21:00") do_devet = devet - p posle_devet = k - devet za_isplatu = minuti_u_sate(do_devet) * 150 + \ minuti_u_sate(posle_devet) * 100 return "Zarada dadilje je: " + str(round(za_isplatu)) + " din."
print(dadilja('18:35','22:50'))
Zarada dadilje je: 546 din.
- funkcija
Program koji računa da li je godina prestupna.
def is_prestupna(godina): # proveravam da li je poslenja godina u veku # da bi bila poslednja u veku to znači da mora biti deljiva sa 100 # a ako je deljiva sa 100 onda je deljiva i sa 400 if godina % 400 == 0: return True # ako nije poslednja godina u veku onda je prestupna ako # je dejiva sa 4 elif godina % 100 != 0 and godina % 4 == 0: return True else: return False
Funkcija vraća
True
ako je godina prestupa, u suprotnom vraćaFalse
.is_prestupna(1983) is_prestupna(1984) is_prestupna(1800) is_prestupna(1900) is_prestupna(2000)
False True False False True
Program koji računa da li je datum validan.
Korišćeni koncepti:
Set — Python reprezentacija matematičkih skupova. Zapisuje se
{}
notacijom.
Funkcijaset(kolekcija)
pretvara druge Python kolekcije u set.Primer jednog seta:
neki_set = {1, 2, 3, 4}
Neke operacije nad setovima:
lista_kolekcija = [1, 2, 3, 4] tuple_kolekcija = (3, 4, 5, 6, 7) A = set(lista_kolekcija) B = set(tuple_kolekcija) # A bez B A - B # B bez A B - A # Unija A i B B.union(A) # presek A i B B.intersection(A) # presek B i A A.intersection(B) A.intersection(B) == B.intersection(A)
>>> >>> >>> >>> >>> ... {1, 2} >>> ... {5, 6, 7} >>> ... {1, 2, 3, 4, 5, 6, 7} >>> ... {3, 4} >>> ... {3, 4} >>> True
Operator
in
— operator pripadanja (membership operator).
Radi za kolekcije i vraćaTrue
kada se element nalazi u kolekciji, a u suprotnomFlase
.1 in [2, 3, 4, 5, 1, 2, 12] 1 in (2, 3, 4, 5, 1, 2, 12) 1 in {2, 3, 4, 5, 1, 2, 12} 12 in range(11, 23)
True True True >>> True
Operator
in
se u uslovima može koristiti kao zamena zaor
lanac.a = 3 a == 1 or a == 2 or a == 3 a in (1, 2, 3) (a == 1 or a == 2 or a == 3) == (a in (1, 2, 3))
>>> True >>> True >>> True
Provera validnosti datuma:
# coding: utf-8 def is_valid(datum): dan, mesec, godina = [int(i.strip()) for i in datum.split("/")] meseci_od_30 = [4, 6, 9, 11] # meseci od 31 su svi između 1 i 12 koji ne spadaju # u one koji imaju 30 dana i nisu februar meseci_od_31 = set(range(1, 13)) - set(meseci_od_30) - set([2]) # ako je u pitanju februar if mesec == 2 and dan > 0: # prestupnim godinama ima 29 dana if is_prestupna(godina) and dan <= 29: return True # kada nije prestupna godina ima 28 dana elif dan <= 28: return True else: return False elif mesec in meseci_od_30 and dan > 0 and dan <= 30: return True elif mesec in meseci_od_31 and dan > 0 and dan <= 31: return True else: return False
is_valid("24/5/1962") is_valid("31/9/2000") is_valid("29/2/2000") is_valid("29/2/2001") is_valid("30/2/2000")
True False True False False
Program koji računa redni broj dana u godini.
U ovom zadatku je korišćena funkcija za računanje prestupne godine koju smo ranije definisali.
def redni_broj_dana(datum): dd, mm, gggg = [int(i.strip()) for i in datum.split("/")] dan_u_godini = 31 * (mm - 1) + dd if mm > 2: dan_u_godini -= (4 * mm + 23) / 10 if is_prestupna(gggg): dan_u_godini += 1 return round(dan_u_godini)
redni_broj_dana("1/1/2000") redni_broj_dana("14/3/2000") redni_broj_dana("31/12/2000") redni_broj_dana("31/12/2001")
1 74 366 365
8 Petlje i logički izrazi
Tabela subjektivnog osećaja hladnoće
def sub_osecaj(t, v): return 3.74 + 0.6215 * t - 35.75 * (v**0.16) + \ 0.4275 * t * (v**0.16) def napravi_tabelu(min_v, max_v, min_t, max_t): temperatures = range(min_t, max_t + 1) # prvi red first_row = [" "] + ["t=" + str(i) for i in temperatures] print("\t".join(first_row)) # ostali redovi for v in range(min_v, max_v + 1): # deo sa v = ... vetar = "v=" + str(v) # ostale kolone temps = ["{0:.3f}".format(sub_osecaj(t, v)) for t in temperatures] # ceo red red = "\t".join([vetar] + temps) print(red)
napravi_tabelu(0, 10, 5, 10)
t=5 t=6 t=7 t=8 t=9 t=10 v=0 6.848 7.469 8.091 8.712 9.334 9.955 v=1 -26.765 -25.716 -24.667 -23.618 -22.569 -21.520 v=2 -30.707 -29.608 -28.509 -27.410 -26.311 -25.212 v=3 -33.224 -32.093 -30.962 -29.831 -28.700 -27.569 v=4 -35.112 -33.957 -32.802 -31.647 -30.491 -29.336 v=5 -36.637 -35.463 -34.288 -33.113 -31.939 -30.764 v=6 -37.924 -36.733 -35.542 -34.352 -33.161 -31.970 v=7 -39.042 -37.837 -36.632 -35.427 -34.222 -33.017 v=8 -40.033 -38.816 -37.598 -36.380 -35.162 -33.945 v=9 -40.925 -39.696 -38.467 -37.238 -36.009 -34.780 v=10 -41.737 -40.498 -39.258 -38.019 -36.780 -35.540
Koliko treba da prođe godina da bi se novac u banci udvostručio
def broj_godina(kamata): ulog = 1 dvostruko = 2 * ulog godina = 0 while ulog < dvostruko: ulog += ulog * kamata godina += 1 print("U godini " + str(godina) + " ulog se uvećao na " + str(ulog)) print("Da bi se ulog udvostručio potrebno je da prođe " + str(godina) + " godina.")
broj_godina(0.04)
U godini 1 ulog se uvećao na 1.04 U godini 2 ulog se uvećao na 1.0816000000000001 U godini 3 ulog se uvećao na 1.124864 U godini 4 ulog se uvećao na 1.16985856 U godini 5 ulog se uvećao na 1.2166529024 U godini 6 ulog se uvećao na 1.265319018496 U godini 7 ulog se uvećao na 1.3159317792358398 U godini 8 ulog se uvećao na 1.3685690504052734 U godini 9 ulog se uvećao na 1.4233118124214843 U godini 10 ulog se uvećao na 1.4802442849183437 U godini 11 ulog se uvećao na 1.5394540563150774 U godini 12 ulog se uvećao na 1.6010322185676804 U godini 13 ulog se uvećao na 1.6650735073103877 U godini 14 ulog se uvećao na 1.7316764476028033 U godini 15 ulog se uvećao na 1.8009435055069154 U godini 16 ulog se uvećao na 1.872981245727192 U godini 17 ulog se uvećao na 1.9479004955562795 U godini 18 ulog se uvećao na 2.025816515378531 Da bi se ulog udvostručio potrebno je da prođe 18 godina.
Sirakuza sekvenca
def sirakuza(start): ret_val = [start] num = start while num != 1: if num % 2 == 0: num = num / 2 else: num = 3 * num + 1 ret_val.append(int(num)) return ret_val
sirakuza(5)
[5, 16, 8, 4, 2, 1]
Provera da li je broj prost
import math as m def is_prost(broj): for i in range(2, int(m.sqrt(broj)) + 1): if broj % i == 0: return False return True
is_prost(127) is_prost(123) is_prost(2) is_prost(24) is_prost(13) is_prost(15) is_prost(9)
True False True False True False False
Funkcija koja vraća listu prostih brojeva koji su manji od prosleđeno argumenta
def get_prosti_to(broj): ret_val = [] for i in range(1, broj): if is_prost(i): ret_val.append(i) return ret_val
get_prosti_to(20)
[1, 2, 3, 5, 7, 11, 13, 17, 19]
Isti zadatak ali uz korišćenje list comprehensions.
def get_prosti_to(broj): return [i for i in range(1, broj) if is_prost(i)]
get_prosti_to(20)
[1, 2, 3, 5, 7, 11, 13, 17, 19]
Najveći zajednički delilac po Euklidovom algoritmu
def nzd(m, n): while m != 0: m_staro = m n_staro = n n = m_staro m = n_staro % m_staro return n
nzd(15, 25) nzd(15, 25)
5 5
8.1 get_prost_to
uz korišćenje filter funkcije. za_radoznale
def get_prost_to(broj): filter(is_prost, range(1, broj))
get_prosti_to(20)
[1, 2, 3, 5, 7, 11, 13, 17, 19]
filter
dakle kao parametar prima funkciju i neku strukturu kroz
koju se može "prolaziti". Vraća samo one elemente strukture za koje
prosleđena funkcija vraća istinitosno tačnu vrednost
(npr. True
). Pogledaj poglavlje o funkcijama višeg reda u
dodatku.
8.2 is_prost
uz korišćenje funkcija višeg reda za_radoznale
import math as m def is_prost(broj): return not any(filter(lambda x: broj % x == 0, range(2, int(m.sqrt(broj)) + 1)))
is_prost(127) is_prost(123) is_prost(2) is_prost(24) is_prost(13) is_prost(15)
True False True False True False
8.3 Zadatak koji liči na projekat
Korisnik se prijavljuje na sistem. Nakon toga unosi
proizvode. Svaki put kada unese neki proizvod program mu ispisuje
sve proizvode koji se nalaze na spisku. Program se prekida kada
korisnik unese quit
za naziv, količinu ili cenu nekog proizvoda.
Spisak registrovanih korisnika se nalazi u korisnici.register
a
spisak proizvoda je u proizvodi.vezbe_7
.
# coding: utf-8 import os delimiter = "|" def prijava(u_name, pasw): with open("korisnici.register", 'r') as f: for l in f.readlines(): if [u_name, pasw] == [s.strip() for s in l.split(delimiter)]: return True return False def prikazi_proizvode(fajl): with open(fajl, 'r') as p: for l in p.readlines(): print("\t".join([i.strip() for i in l.split(delimiter)])) def program(): u_name = input("Unesite korisničko ime: ") pasw = input("Unesite lozinku: ") if not prijava(u_name, pasw): print("Pogrešan par korisničkog imena i lozinke") return while True: naziv = input("Unesite naziv proizvoda: ") if naziv == "quit": return cena = input("Unesite cenu proizvoda: ") if cena == "quit": return kolicina = input("Unesite količinu proizvoda: ") if kolicina == "quit": return # izbacim whitespace karaktere sa kraja i pocetka naziv, cena, kolicina = [s.strip() for s in [naziv, cena, kolicina]] with open("proizvodi.vezbe_7", 'a') as p: p.write(delimiter.join([naziv, cena, kolicina]) + os.linesep) # prikazujem sve proizvode prikazi_proizvode("proizvodi.vezbe_7")
Program se pokreće sa:
program()
9 Kolekcije podataka
Ispis i dodavanje knjiga
Korišćeni koncepti:
zip
funkcija koju smo spominjali ranije.String u više linija.
Ako vam je neka sekvenca građenja stringa preduga:
s = "a" + "b" + "c" + "d" + "e" + "f" + "g" + "h"
Možete je napisati u više redova na sledeći način:
s = ("a" "b" "c" "d" "e" "f" "g" "h")
# Ova globalna varijabla simulira fajl u kome se nalaze podaci o knjizi knjige = [] def dodaj_knjigu(k): knjige.append(k) def ispisi_sve_knjige(): # string u više linija zaglavlje = ("id" "\t" "naslov" "\t" "autori" "\t" "izdavac" "\t" "cena" "\t" "kolicina" "\t" "godina") print(zaglavlje) print(int(3/2 * len(zaglavlje)) * "-") for k, i in zip(knjige, range(len(knjige) + 1)): print(str(i + 1) + "\t" + k["naslov"] + "\t" + k["autori"] + "\t" + k["izdavac"] + "\t" + str(k["cena"]) + "\t" + str(k["kolicina"]) + "\t" + str(k["godina"]))
dodaj_knjigu({"naslov": "N1", "autori": "A1, A2", "izdavac": "I1", "cena": 123.23, "kolicina": 12, "godina": 2016}) dodaj_knjigu({"naslov": "N2", "autori": "A11, A22", "izdavac": "I2", "cena": 123.23, "kolicina": 12, "godina": 2015}) ispisi_sve_knjige()
id naslov autori izdavac cena kolicina godina ------------------------------------------------------------------- 1 N1 A1, A2 I1 123.23 12 2016 2 N2 A11, A22 I2 123.23 12 2015
Isti zadatak ali sa čitanjem i pisanjem u fajl
Korišćeni koncepti:
Opcioni imenovani parametri
Python ima mogućnost pisanja funkcija koje imaju opcione parametre.
Opcioni parametri su oni koji funkciji mogu ali ne moraju biti prosleđeni pri pozivu.
Python pruža tri tipa takvih parametara:
Imenovani
def foo_a(obavezan, opcioni1="nisi prosledio opcioni1", opcioni2="nisi prosledio opcioni1"): print(obavezan) print(opcioni1) print(opcioni2)
foo_a("ovo moram da prosledim")
ovo moram da prosledim nisi prosledio opcioni1 nisi prosledio opcioni1
foo_a("ovo moram da prosledim", "prosleđujem opcioni1")
ovo moram da prosledim prosleđujem opcioni1 nisi prosledio opcioni1
foo_a("ovo moram da prosledim", "prosleđujem opcioni1", "prosleđujem opcioni2")
ovo moram da prosledim prosleđujem opcioni1 prosleđujem opcioni2
Neimenovani
def foo_b(obavezni, *args): print(obavezni) print("ostali parametri: ") print(*args)
foo_b("ovo mora")
ovo mora ostali parametri:
foo_b("ovo mora", 1, 2, 3, "pera", {'zivotinja': 'zec'})
ovo mora ostali parametri: 1 2 3 pera {'zivotinja': 'zec'}
Ključne reči
def foo_c(obavezni, **kwargs): print(obavezni) for (k, v) in kwargs.items(): print(k, v)
foo_c("ovo mora")
ovo mora
foo_c("ovo mora", ime1="vrednost1", ime2=2, ime3=[1, 2, 3])
ovo mora ime2 2 ime3 [1, 2, 3] ime1 vrednost1
Funkcija naravno može od jednom da ima sve navedene tipove opcionih parametara ( ukoliko to ima smisla ).
*args
i**kwargs
su samo nazivi. Sve što počinje sa*
i**
se tretira kao skup neimenovanih i imenovanih opcionih parametara, respektivno.def foo_d(obavezni, *neimenovani_parametri, imenovani="default value", **kljucne_reci): print(obavezni) print("Neimenovani opcioni:") print(neimenovani_parametri) print("Imenovani parametri:") print(imenovani) print("Ključne reči:") for (k, v) in kljucne_reci.items(): print(k, v)
foo_d("ovo mora", 1, 2, "neimenovani", imenovani="prosleđujem imenovani", kljucna_rec_1="k1", kljucna_rec_2="k2", kljucna_rec_3=33)
ovo mora Neimenovani opcioni: (1, 2, 'neimenovani') Imenovani parametri: prosleđujem imenovani Ključne reči: kljucna_rec_3 33 kljucna_rec_1 k1 kljucna_rec_2 k2
foo_d("ovo mora", 1, 2, imenovani="prosleđujem imenovani")
ovo mora Neimenovani opcioni: (1, 2) Imenovani parametri: prosleđujem imenovani Ključne reči:
foo_d("ovo mora", imenovani="prosleđujem imenovani")
ovo mora Neimenovani opcioni: () Imenovani parametri: prosleđujem imenovani Ključne reči:
foo_d("ovo mora", k_rec = "ovo je kljucna rec")
ovo mora Neimenovani opcioni: () Imenovani parametri: default value Ključne reči: k_rec ovo je kljucna rec
Ne mogu neimenovani argumenti ići posle imenovanih parametara ili posle ključnih reči:
foo_d("ovo mora", imenovani="prosleđujem imenovani", "opcioni")
File "<stdin>", line 1 SyntaxError: non-keyword arg after keyword arg
foo_d("ovo mora", k_rec = "ovo je kljucna rec", 12)
File "<stdin>", line 1 SyntaxError: non-keyword arg after keyword arg
if
u jednom redu
ako_je_uslov_tačan if uslov else ako_uslov_nije_tačan
a = 4 a = "jeste veće od 5" if a > 5 else "nije veće od 5"
print(a)
nije veće od 5
Implementacija:
import os # ova funkcija koristi imenovane opcione parametre def file_to_list(f_name, delimiter='|', numericki=["cena", "kolicina", "godina"]): with open(f_name, 'r') as f: knjige = [] # prolazim kroz svaku liniju for l in f.readlines(): # u svakoj liniji je po jedna knjiga k = {} # liniju splitujem po delimiteru i sklonim whitespace # karaktere sa kraja i početka podaci_u_liniji = [d.strip() for d in l.split(delimiter)] # pogledaj šta je zip ranije for osobina, podaci in zip(["naslov", "autori", "izdavac", "cena", "kolicina", "godina"], podaci_u_liniji): # koristim if u jednom redu # da pretvodim numeričke podatke u brojeve # i izgrađujem rečnik koji predstavlja jednu knjigu k[osobina] = float(podaci) if osobina in numericki else podaci # dodam knjigu knjige.append(k) return knjige def list_to_file(li, f_name, delimiter="|"): with open(f_name, 'a') as f: lines = [] for dic in li: # preuzmem sve podatke iz rečnika po standardnom redosledu line_data = [dic["naslov"], dic["autori"], dic["izdavac"], dic["cena"], dic["kolicina"], dic["godina"]] # napravim liniju za snimanje u fajl lines.append(delimiter.join([str(v) for v in line_data]) + os.linesep) f.writelines(lines) def dodaj_knjigu(k): list_to_file([k], "knjige.data") def ispisi_sve_knjige(f_name): zaglavlje = ("id" "\t" "naslov" "\t" "autori" "\t" "izdavac" "\t" "cena" "\t" "kolicina" "\t" "godina") print(zaglavlje) print(int(3/2 * len(zaglavlje)) * "-") knjige = file_to_list(f_name) for k, i in zip(knjige, range(len(knjige) + 1)): print(str(i + 1) + "\t" + k["naslov"] + "\t" + k["autori"] + "\t" + k["izdavac"] + "\t" + str(k["cena"]) + "\t" + str(k["kolicina"]) + "\t" + str(k["godina"]))
dodaj_knjigu({'naslov': 'Novi', 'kolicina': 12.0, 'godina': 2016, 'izdavac': 'Novi Izdavac', 'autori': 'Novi Novic', 'cena': 123.23}) ispisi_sve_knjige("knjige.data")
id naslov autori izdavac cena kolicina godina ------------------------------------------------------------------- 1 SICP Sussman I1 12.0 12.0 1979.0 2 SICP Sussman I1 12.0 12.0 1979.0 3 Novi Novi Novic Novi Izdavac 123.23 12.0 2016.0
S obzirom da ovde knjiga nema eksplicitan
id
već je on predstavljen indeksom u listi to znači da ne možemo znati da li su SICP i SICP jedna te ista knjiga i zato je moguće da se u spisku knjiga nađe knjiga koja se zove SICP više puta.Ako želimo da rešimo ovaj problem onda moramo napraviti funkciju
dodaj_knjigu
takvu da prima knjigu koja eksplicitno sadržiid
. To stvara novi zadatak — potrebno je voditi računa da se u našem spisku knjiga ne nađu dve sa istimid
.
10 Dodatak
10.1 Rekurzija
Rekurzija je kada neki pojam definišemo koristeći sam pojam.
U programskim jezicima rekurzija se može postići na nivou funkcija:
def reci_ciao(): print("Ciao!") reci_ciao()
reci_ciao()
Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! ... Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in reci_ciao File "<stdin>", line 3, in reci_ciao File "<stdin>", line 3, in reci_ciao File "<stdin>", line 3, in reci_ciao File "<stdin>", line 3, in reci_ciao File "<stdin>", line 3, in reci_ciao File "<stdin>", line 3, in reci_ciao ... RuntimeError: maximum recursion depth exceeded while calling a Python object
Računari su konačne prirode pa ne mogu (tek tako) izraziti nešto
što je beskonačno, a poziv funkcije reci_ciao
je beskonačan.
Kako bi se rekurzija nekada završila moramo joj dati uslov završetka:
def reci_ciao(p): # ovo je uslov završetka if p < 10: print("Ciao!") reci_ciao(p + 1) else: return
reci_ciao(0)
Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao!
reci_ciao(7)
Ciao! Ciao! Ciao!
reci_ciao(10)
reci_ciao(15)
10.1.1 Rekurzija kao imperativna petlja
Rekurzija se često može napisati kao imperativna petlja:
def reci_ciao(p): for i in range(p, 10): print("Ciao!")
reci_ciao(0)
Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao! Ciao!
reci_ciao(7)
Ciao! Ciao! Ciao!
reci_ciao(10)
reci_ciao(15)
Imperativna petlja troši konstantnu količinu
memorije. Neoptimizovana rekurzija troši novu memoriju pri svakom
pozivu, dakle — više ulazaka u funkciju (poziva) više memorije
potrošeno.
Rekurzija u Python-u je neoptimizovana (u svojoj osnovnoj
verziji) ali to nije situacija sa svim programskim jezicima.
10.2 Python modularizacija
Svi netrivijalni Python programi su podeljeni u delove.
10.2.1 Moduli
Svaki Python file je ujedno i modul. Svaki modul bi trebao da sadrži kod koji čini neku veću logičku celinu.
Ime svakog modula odgovara imenu fajla bez ekstenzije py
. Dakle,
ako imamo fajl prijava.py
onda se modul zove prijava
.
Svaki modul ima ime __main__
ako je pokrenut direktno kao
skripta:
python3 prodavnica.py
U svakom fajlu možete doći do imena njegovog modula preko globalne
varijable __name__
:
print("Ime ovog modula je: " + __name__)
Ime ovog modula je: __main__
… zato što je ovaj modul pokrenut direktno kao skripta.
10.2.2 Paketi
Svaki direktorijum u strukturi Python projekta može biti paket
ako sadrži prazan fajl sa nazivom __init__.py
.
Subpaketi(podpaketi) nekog modula su takođe direktorijumi koji sadrže prazan
__init__.py
fajl.
projekat/ Top-level paket __init__.py Inicijalizator top-level paketa prodavnica.py formatiranje/ Subpaket za formatiranje __init__.py tabela.py ascii_slika.py ... prijava/ Subpaket za proijavu __init__.py admin.py ostali.py ...
Sadržaj fajla projekat/prijava/admin.py
je:
def prijavi_se(u_name, pasw): return "Korisnik " + u_name + ", " + pasw + " se prijavljuje."
Ako želimo da koristimo funkciju koja je definisana u
projekat/prijava/admin.py
iz projekat/prodavnica.py
onda u
projekat/prodavnica.py
treba da stoji sledeće:
import prijava.admin prijava.admin.prijavi_se("pera", "peric")
>>> 'Korisnik pera, peric se prijavljuje.'
Ovakvo ime funkcije, koje u sebi sadrži potpunu informaciju o tome gde je tačno funkcija definisana se zove fully qualified name. Možemo to prevesti sa puno ime funkcije, za funkciju iznad puno ime je:
prijava.admin.prijavi_se
Ili, ako želite da modul za prijavljivanje administratora zovete
a_prijava
:
import prijava.admin as a_prijava a_prijava.prijavi_se("pera", "peric")
>>> 'Korisnik pera, peric se prijavljuje.'
a_prijava
se još naziva i alias modula.
Možete uključiti funkciju i bez ikakvog aliasa ili punog imena koristeći sledeću konstrukciju:
from putanja.do.modula import funkcija
from prijava.admin import prijavi_se prijavi_se("pera", "peric")
>>> 'Korisnik pera, peric se prijavljuje.'
Kako funkcioniše import? Tako što Python ode na putanju na koju ste ukazali imenom modula i prosto izvrši taj fajl. Obratite pažnju na ovo!
Ako imao fajl
projekat/prijava/admin_pogresan.py
:def prijavi_se(u_name, pasw): return "Korisnik " + u_name + ", " + pasw + " se prijavljuje" print("Neki potpuno bespotreban print.")
... >>> Neki potpuno bespotreban print.
I ako njega uključimo da bismo koristili njegovu funkciju
prijavi_se
pri tome će nam se ispisati i bespotreban tekst!
import prijava.admin_pogresan prijava.admin_pogresan.prijavi_se("pera", "peric")
Neki potpuno bespotreban print. 'Korisnik pera, peric se prijavljuje'
10.2.3 Primena modula i paketa
Python fajl se često piše tako da se može koristiti i direktno kao skripta i tako što će biti uključen kao modul.
Za onaj kod koji treba biti izvršen samo kada se fajl pokreće direktno kao skripta koristi se sledeći idiom:
if __name__ == '__main__': # kod koji se izvršava kada je fajl pokrenut kao skripta print("Pokrenut kao skripta")
Pretpostavimo da imamo fajl prikaz.py
i da ga pokrećemo kao
skriptu:
python3 prikaz.py
# fajl prikaz.py def javi_se(): return "Dobar dan, ja sam modul prikaz" def za_direktno(): print("Pokrenut sam direktno kao skripta!") def pozdravi_se(): return "Doviđenja, ja sam modul prikaz" if __name__ == '__main__': za_direktno()
>>> ... ... >>> ... ... >>> ... ... >>> ... ... Pokrenut sam direktno kao skripta!
Taj modul takođe možemo uključiti sa import
i pri tome se neće
ispisati ništa.
10.3 lambda funkcije
Lambda funkcije potiču iz formalnog sistema u matematičkoj logici koji se zove lambda račun. Taj sistem je uveo još Alonzo Črč početkom 30-tih godina prošlog veka. U programske jezike je uveden kroz Lisp sredinom 50-tih godina prošlog veka.
Dakle, stvar je prilično stara (za istoriju računarstva) iako se iz nekog čudnog razloga danas smatra novinom u programskim jezicima koji je naknadno uvode.
10.3.1 Šta je to?
Lambda funkcije zovemo još i neimenovanim jer nemaju ime po kome bismo mogli naknadno da ih identifikujemo.
Primer jedne imenovane, "obične" funkcije:
def inc(x): return x + 1
Poziv imenovane funkcije:
inc(12)
13
Korišćenjem lambda izraza istu ovu funkciju možemo definisati na sledeći način:
inc_prim = lambda x: x + 1
I pozivati je na potpuno uobičajen način:
inc_prim(12)
13
Dakle, lambda izraz ima sledeću formulaciju:
lambda param1, param2, param3 ... : izraz
Gde će rezultat izraz
-a biti vraćen kao rezultat izvršavanja
čitave lambda funkcije.
Na primer izraz x^2 + 3*x + 2
možemo zapisati:
lambda x : x**2 + 3 * x + 2
<function <lambda> at 0x7f6de72afae8>
Međutim ovde lambda izraz nismo vezali ni za kakvo ime pa se ne možemo kasnije referisati na njega.
Ako želimo da definišemo funkciju i na istom mestu joj prosledimo parametre i pozovemo je to možemo uraditi na sledeći način:
(lambda x : x**2 + 3 * x + 2)(2)
12
10.3.2 Funkcije višeg reda
Funkcije višeg reda (Higher-order functions) su one funkcije koje ispunjavaju barem jedan od uslova:
- Uzimaju jednu ili više funkcija za argumente
- Vraćaju funkciju kao rezultat
- Neke funkcije višeg reda
Neki standardni problemi u Python-u se idiomatski rešavaju korišćenjem funkcija višeg reda.
Sortiranje kolekcija:
k = [{'ime': "Bananica", 'kalorije': 128}, {'ime': "Jaffa keks", 'kalorije': 178}, {'ime': "Munchmallow", 'kalorije': 104}, {'ime': "Plazma keks",'kalorije': 202}]
sorted(k, key=lambda x: x['kalorije'])
[{'kalorije': 104, 'ime': 'Munchmallow'}, {'kalorije': 128, 'ime': 'Bananica'}, {'kalorije': 178, 'ime': 'Jaffa keks'}, {'kalorije': 202, 'ime': 'Plazma keks'}]
[napomena]
Svakoj funkciji koja prima neku drugu funkciju kao parametar možete proslediti i imenovanu ("običnu") funkciju.def sort_funkcija(x): return x['kalorije']
sorted(k, key=sort_funkcija)
[{'kalorije': 104, 'ime': 'Munchmallow'}, {'kalorije': 128, 'ime': 'Bananica'}, {'kalorije': 178, 'ime': 'Jaffa keks'}, {'kalorije': 202, 'ime': 'Plazma keks'}]
Razlog zbog koga se u funkcijama višeg reda češće koriste lambda izrazi je zato što se funkcije koje se prosleđuju kao parametri obično neće više nigde koristiti pa nema ni potrebe da im dajemo ime.
Filtriranje kolekcija:
Želimo da dobijemo samo one slatkiše koji imaju više od 110 kalorija:
filter(lambda x: x['kalorije'] > 110, k)
<filter object at 0x7f6de71ff828>
O! Ne! Šta je ovo?! Gde je lista?!
filter
funkcija vraća lenju sekvencu kao što npr. radirange
koju smo spominjali ranije. Da bismo dobili evaluiranu (već formiranu) listu od lenje sekvence možemo je naterati da se pretvori u listu korišćenjemlist
funkcije:list(filter(lambda x: x['kalorije'] > 110, k))
[{'kalorije': 128, 'ime': 'Bananica'}, {'kalorije': 178, 'ime': 'Jaffa keks'}, {'kalorije': 202, 'ime': 'Plazma keks'}]
Map funkcija
map
funkcija radi nešto slično kao list comprehensions koji smo pominjali ranije samo što je funkcija višeg reda i što vraća lenju sekvencu.map(lambda x: x**2, [1, 2, 3])
<map object at 0x7f6de72040b8>
list(map(lambda x: x**2, [1, 2, 3]))
[1, 4, 9]
nums = [1, 2, 3, 4, 5]
list(map(lambda x: x**2, nums)) == [x**2 for x in nums]
True
11 Korisne biblioteke
Da biste instalirali nove Python biblioteke potrebno je da
prethodno na Vašem sistemu imate alat koji to ume da radi. Standard
u Python-u je pip
. Ako ste instalirali Python na neki od
standardnih načina onda pip
već imate instaliran, potrebno je samo
da ga upgrade-ujete.
11.1 tabulate
Instalacija:
pip install tabulate
Korišćenje:
from tabulate import tabulate headers = [["prvo", "drugo", "treće"]] tabela = [[11, 12, 13], [21, 22, 23], [31, 32, 33]]
print(tabulate(tabela))
-- -- -- 11 12 13 21 22 23 31 32 33 -- -- --
print(tabulate(headers + tabela, headers='firstrow'))
prvo drugo treće ------ ------- ------- 11 12 13 21 22 23 31 32 33
print(tabulate(headers + tabela, headers='firstrow', tablefmt='grid'))
+--------+---------+---------+ | prvo | drugo | treće | +========+=========+=========+ | 11 | 12 | 13 | +--------+---------+---------+ | 21 | 22 | 23 | +--------+---------+---------+ | 31 | 32 | 33 | +--------+---------+---------+