Uvod u Python klase

Klase su način da se podaci i funkcije povežu u jednu celinu. Kreiranjem nove klase kreira se novi tip objekata, čime se omogućavaju nove instance objekata novostvorenog tipa. Svaka instanca klase može imati atribute koji opisuju stanje instance. Instance klase mogu takođe da imaju i metode (definisane u klasi) kojim se modifikuju njihova unutrašnja stanja.

U poređenju sa drugim programskim jezicima, mehanizam klasa u Pythonu dodaje nove klase uz minimalno novu sintasku i semantiku. Ovaj mehanizam je mešavina mehanizama koji se mogu naći u C++ i Modula-3 programskim jezicima.

Klase u Pythonu imaju sve standardne karakteristike objektno orjentisanog programiranja: mehanizam nasleđivanja omogućava nasleđivanje iz više bazičnih klasa, a metode mogu da pozivaju metode bazičnih klasa koje imaju isto ime kao metode izvedene klase. Objekti mogu da imaju neograničen broj i vrste podataka. Kao i u slučaju modula, klase u Python-u imaju dinamičku prirodu: kreiraju se u vreme izvršavanja programa i mogu biti modifikovane nakon kreiranja.

Koristeći C++ treminologiju može se reći da su članovi klase ( uključujući podatke ) tipa public, a sve klasi pripadajuće funkcije tipa virtual. Kao i kod Modula-3 jezika, nema skraćenog referenciranja na metode iz drugih metoda iste klase: funkcije (metode) se deklarišu eksplicitno sa prvim argumentom koji predstavlja sam objekat, koji se implicitno dodeljuje pri pozivu metode. Kao kod Smalltalk jezika, i same klase su objekti. Za razliku od C++ i Modula-3, ugrađeni operatori mogu se koristiti kao bazne klase za proširenje od strane korisnika. Takođe, kao i kod C++ većina ugrađenih operatora sa specijalnom sintaksom (aritmetički operatori, indeksi itd.) mogu biti redefinisani za instance klase.

(Zbog nedostatka univerzalno prihvaćene terminologije kojom se govori o klasama, ovde ćemo povremeno koristiti izraze pozajmljene od Smalltalk i C++ jezika).

O imenima i objektima

Objeki imaju svoju individualnost, ali jednom objektu može biti pridruženo više imena. Ovo je poznato kao “davanje nadimaka” u drugim jezicima. Ovo se kod Pythona ne preporučuje, i može biti ignorisano za slučaj nepromenljivih bazičnih tipova (brojevi, stringovi, tuplovi). Međutim, davanje nadimaka može u Python-u izazvati neočekivane efekte kada se primeni na promenljive objekte kao što su liste, rečnici i ostali. To se ipak može koristiti na dobrobit programa, pošto se nadimci ponašaju, na neki način, kao pointeri. Na primer, prenos objekata je efikasan pošto se samo pointer na objekat prenosi kao argument funkcije; pa ako funkcija modifikuje objekat koji joj je prenet kao argument, pozivač funkcije će videti tu promenu – što eliminiše potrebu za prenos dva argumenta kako je slučaj recimo kod Pascal-a.

Python Scopes and Namespaces

Pre nego što počnemo sa klasama, treba da se upoznamo sa Python “scope” pravilima. Definicije klase koriste neke fine trikove sa “namespace-ima”, pa je potrebno da znamo kako “scope” i “namespace” funkcionišu da bi u potpunosti razumeli šta se događa.

Počećemo sa nekim definicijama.

Reč namespace označava preslikavanje iz imena u objekte. Većina “namespace-a” se u Pythonu implementira putem rečnika. Primeri namespace-a su: skup ugrađenih imena (koji sadrži funkcije kao što je abs, ili ugrađene izuzetke); globalna imena u modulima; lokalna imana u funkcijama. Na neki način skup atributa jednog objekta takođe predstavlja namespace. Ono što je važno znati o namespace-u je da imena u razičitim namespace-ovima nemaju nikakvu međusobnu vezu; na primer dva različita modula mogu imati funkciju poda nazivom “maximize” a da to ne stvara nikakvu zabunu – korisnik modula mora da stavi ime modula kao prefiks na funkciju.

Uzgred, koristićemo reč attribute za svako ime koje sledi posle tačke (dot) – na primer, u izrazu “z.real”, “real” je atribut objekta “z”. Strogo govoreći, reference na imena u modulima su atributske: u izrazu “modname.funcname”, “modname” je modul a “funcname” je njegov atribut.

Atributi mogu da budu samo za čitanje i/ili za pisanje (read-only ili writable). U drugom slučaju (writable), moguća je naredba pridruživanja. Atributi modula su “writable”: možete pisati “modname.the_answer = 42”. Modul atributi mogu biti i brisani: možete napisati “del” naredbu. Na primer “del modname.the_answer” će izbrisati atribut “the_answer” iz objekta pod imenom “modname”.

Namespace se kreiraju u raznim trenucima i imaju različite dužine trajanja. Namespace koji sadrži ugrađena imena se kreira kada se startuje Python interpreter i nikad se ne briše. Globalni namespace za modul se kreira kada se definicija modula učita; u normalnim okolnostima namespace modula traje sve dok interpreter ne prestane sa radom. Naredbe koje se izvršavaju sa navišeg nivoa poziva intrepretera, bilo da se čitaju iz skripte bilo da su interaktivne, smatraju se delom modula koji se zove “__main__”, zato one imaju svoj globalni namespace. (Ugrađena imena u stvari borave u modulu koji se zove “builtins”).

Lokalni namespace za funkciju kreira se kada se funkcija pozove, a briše se kada funkcija vrati rezultat ili kada se pojavi izuzeće koje nije opsluženo u funkciji. Naravno, svaki rekurzivni poziv ima svoj lokalni namespace.

Reč scope označava tekstualni region Python programa u kome je namespace direktno pristupačan. Pod “direktno pristupačnim” podrazumeva se nekvalifikovana referenca na pokušaj pronalaženja imena u namespace-u.

Iako se “scope” određuju statički, koriste se dinamički. U bilo koje vreme tokom izvršavanja, postoje najmanje tri ugnježdena “skopes”- čija su “namespaces” direktno pristupačna:

  1. “scope” funkcije koja ima lokalna imena
  2. “scope” modula koji ima globalna imena
  3. spoljašnji “scope” koji ugrađena (builtins) imena

Ovo se može ilustrovati sledećom slikom:

_images/scopes.jpg

Scopes and Namespaces primer

Ovo je jedan primer koji pokazuje kako se gore navedena pravila primenjuju na Python varijablama:

def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        global spam
        spam = "global spam"

    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

Izlaz iz ovog programa je sledeći:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

Zbog važnosti razumevanja scope i namespace pojmova dajemo još jedan primer koji ilustruje hijerahiju sa sledeće slike:

_images/scope_resolution.png
a = 'global'

def outer():

    def len(in_var):
        print('called my len() function: ', end="")
        l = 0
        for i in in_var:
            l += 1
        return l

    a = 'local'

    def inner():
        global len
        nonlocal a
        a += ' variable'
    inner()
    print('a is', a)
    print(len(a))


outer()

print(len(a))
print('a is', a)

Pitanja

1. Napišite Python funkciju is_multiple(n, m) koja prihvata dve celobrojne vrednosti i vraća True ako je n umnožak od m, to jest n=m*i za neki ceo broj i, u suprotnom funkcija vraća False.

2. Napišite kratku Python funkciju is_even(k) koja prihvata celobrojnu vrednost i vraća True ako je k paran, a False ako nije. Međutim, vaša funkcija ne sme da koristi operacije množenja, deljenja po modulu i običnog deljenja.

3. Napišite kratku Python funkciju minmax(data) koja prihvata sekvencu jednog ili više brojeva i vraća najmanji i najveći broj u formi tupla (torke) dužine dva. Ne smete da korsitite built-in funkcije min i max pri implementaciji vašeg rešenja.

4. Napišite kratku Python funkciju koja prihvata pozitivan ceo broj n i vraća sumu kvardrata svih pozitivnih celih brojeva manjih od n.

5. Napišite jednom naredbom kako se može izračunati suma iz zadatka 4, oslanjajuči se na Python sintaksu za komponovanje liste (list comprehension) i na built-in funkciju sum.

6. Napišite kratku Python funkciju koja prihvata pozitivan ceo broj n i vraća sumu kvadrata svih neparnih pozitivnih brojeva manjih od .

7. Napišite jednom naredbom kako se može izračunati suma iz zadatka 4, oslanjajuči se na Python sintaksu za komponovanje liste (list comprehension) i na built-in funkciju sum.

8. Napišite naredbu kojom se korišćenjem Python sintaksu za komponovanje liste (list comprehension) generiše lista [1, 2, 4, 8, 16, 32, 64, 128, 256].

Sve zadatke i rešenja postaviti u jedan .py fajl. Na kraju fajle napisati naredbe za testiranje ispravnosti rada napisanih funkcija.