14.5 Promenljive koje pripadaju klasi ili objektu
Sve do sada, naglasak mog rada sa klasama i objektima je bio na metodama, tj na funkcionalnim delovima klasa i objekata. Sada želim da malo razjasnim i njihov deo koji se bavi podacima - varijablama. Kao što si možda do sada primetio, delovi koji „nose“ podatke, koje zovemo polja, u suštini nisu ništa drugo nego varijable, koje smo mi povezali sa klasom ili objektom pomoću specijelne oznake (self, ili bez) ili položaja (varijabla se nalazi u definiciji klase, to je varijabla te klase). Pristup tim varijablama izvan klase ili objekta (tj iz samog programa) radimo pomoću tzv specifikacije imena (namespaces) za klase i objekte. To nedvosmisleno govori Pythonu da imena promenljivih važe samo unutar određenih klasa ili objekata. Zato se i nazivaju name spaces (eng. Prostor u kome se pojavljuje neko ime).
U prethodnom primeru sam ti pokazao konkretnu primenu namespeca. Npr. self.ime je bila promenljiva objekta klase Devojka. Pristup promenljivoj je bio Devojka().ime().Budući da su sve te promenljive pripadale samo svakom pojedinačnom objektu, važno je napomenuti da to nemora uvek da bude tako. Mi imamo još malko mogućnosti, i sada ćemo ih istražiti.
Postoje dve vrste polja - polja klase i polja objekata, koje su i nazvani po svojoj funkciji - da li varijable pripadaju samoj klasi koja kreira objekte ili samom objektu. Jednostavnije rečeno - da li klasa ili njen objekat poseduje tu varijablu. Sve do sada smo radili sa varijablama koje pripadaju objektu.
Promenljive koje pripadaju klasi su deljive - mogu biti dostupne svim instancama te klase tj njih svaki objekat može da vidi, da joj pristupi i da je promeni. Ovo znači da kada kreiramo neku promenljivu klase postoji samo taj jedan primerak te promenljive i kada neki objekat napravi neku promenu na vrednosti te promenljive, ta promena će momentalno biti vidljiva i dostupna svim drugim objektima koje je kreirala ta klasa.
Promenljive objekta su u vlasništvu svakog individualnog objekta te klase. Kao što smo i u primeru sa devojkama videli svaki naš kreirani objekat ima svoj primerak polja (svoju promenljivu koja nosi osobinu), a to isto znači da objekti ne dele svoje osobine, niti imaju ikakvog uticaja na promenljivu, koja ima isto ime u nekoj drugoj instanci (self.ime za ribu nije isto kao self.ime za stidljivu).
Iako deluje konfuzno, mislim da će primer mnoge stvari objasniti. Ideja je da mi malo glumimo Boga, i da kreiramo neke žive organizme. Nama, kao Bogu, je bitno koliko u određenom trenutku imamo živih organizama na zemlji. Osobine organizma su rođenje i smrt. Svaki organizam koji se rodi povećava broj živih, dok onaj koji umire – smanjuje broj. Očigledno je da broj organizama mora biti varijabla klase. U ovom primeru, koji ćemo da sačuvamo kao organizmi.py, ćemo naučiti i mnogo drugih stvari, zato vreme je da krenemo sa razmišljanjem o rešavanju ovog malog problema koji će da preraste u prilično dugačak primer koji će da pomogne da ti pokažem pravu prirodu varijabli klase i objekata.
Prvo razmišljamo o promenljivama – nama treba jedna promenljiva koju možemo da nazovemo ukupno i ona će da pripada klasi koju ćemo nazvati Organizmi, zato će to biti promenljiva klase. Potrebna nam je i promenljiva, koju ćemo nazvati vrsta koja će da pripada svakom pojedinačnom objektu (što postižemo koristeći prefiks self) i zbog toga će to biti promenljiva objekta.
Iz tog razloga, mi ćemo u svom radu, promenljivoj klase ukupno pristupati pomoću oznake koja uključuje i ime klase – Organizmi.ukupno, a ne kao što smo do sada označavali - self.ukupno. Mi ćemo, unutar metoda (funkcija koje smo definisali u klasi) da pozivamo promenljivu objekta vrsta koristeći oznaku self.vrsta. Ovo je najjednostavnije nešto što treba da zapamtiš - razlika između promenljivih klasa i objekata je u tome da li koristimo oznaku self ili ime klase. Takođe, imaj malu začkoljicu uvek na umu: ukoliko definišemo promenljivu objekta sa istim imenom kao što ima neka promenljiva klase, polje objekta će da „sakrije“ vrednost koju ima promenljiva klase (npr, da želimo da imamo promenljivu vrsta koja bi pripadala klasi)!
Postoji još jedan način. Python daje mogućnost da svaki objekat zna koja ga je klasa stvorila, i za to služi __class__ atribut, mi ne moramo da koristimo oznaku Organizmi.ukupno u našem programu, već, umesto nje možemo da koristimo i self.__class__.ukupno. Mislim da je ipak lakši prvi način – makar imamo manje da kucamo, ali je korisno znati da uvek postoji i neka alternativa.
Sada, kada smo razgraničili da nam u našem programu trebaju samo dve promenljive, vreme je da razmislimo o funkcijama koje treba da implementiramo. Prva funkcija je – rođenje neke životne vrste. Već smo videli da nam za te svrhe služi __init__ metoda. Da bi smo kreirali ijednu životnu vrstu mi ćemo koristiti promenljivu vrsta. Ova metoda nam je korisna jer u njoj možemo da povećamo broj ukupno za 1, jer ćemo dobiti još jednu životnu vrstu.
Sledeća funkcija koja nam je potrebna je smrt životne forme. Mi možemo da je nazovemo smrt(), pa da je u svom radu pozivamo kao Organizmi().smrt(), ali nam Python daje jedno jednostavno rešenje – metodu __del__, koja se u drugim programerskim jezicima zove destruktor. Korišćenjem ovakvog imena (__del__) mi imamo mogućnost da definišemo šta se dešava kada u našem programu koristimo naredbu del – tj kada brišemo neki objekat naše klase.
Znači, za razliku od metode __init__, koji služi za definisanje rađanja, kreiranja, mi ćemo da koristimo __del__ metod, koji služi za definisanje smrti, uništenja, i koji ima super osobinu da će da oslobodi deo kompjuterske memorije koji je uništeni objekat do tada zauzimao. U ovom metodu, mi treba da smanjimo broj Organizmi.ukupno za 1.
Sada da krenemo na pisanje programa:
#!/usr/bin/env python3
'''Primer kako klasa prati objekte.'''
class Organizmi:
'''Klasa koja predstavlja sve životne vrste na zemlji.'''
ukupno = 0
def __init__(self, vrsta):
'''Dobrodošla nam mala vrsto!'''
self.vrsta = vrsta
print('Stvorili smo organizam koji se zove {}'.format(self.vrsta))
Organizmi.ukupno += 1
def __del__(self):
'''Doviđenja mali parazitu.'''
print('{} je parazit.\nSada je uništen.'.format(self.vrsta))
Organizmi.ukupno -= 1
if Organizmi.ukupno == 0:
print('Ups...{} je poslednja životna vrsta.\nPlaneta je uspešno uništena. '.format( self.vrsta ))
else:
print('Na našoj veseloj planeti živi {:d} životnih formi. \nPlaneta je funkcionalna. '.format(Organizmi.ukupno))
def moli(self):
'''Organizam ti je zahvalan na svemu.'''
print('Hvala ti na svemu, ja sam mali {} i ovde mi je teško. Bolje me ubij...'.format(self.vrsta))
@classmethod
def koliko(klasa):
'''Koliko imamo organizama.'''
print('Trenutno je {:d} organizama na planeti.'.format(klasa.ukupno))
Što nam, pokrenuto u interaktivnom promptu daje izlaz:
>>> import organizmi
>>> ameba = organizmi.Organizmi('ameba')
Stvorili smo organizam koji se zove ameba
>>> ameba.moli()
Hvala ti na svemu, ja sam mali ameba i ovde mi je teško. Bolje me ubij...
>>> organizmi.Organizmi.koliko()
Trenutno je 1 organizama na planeti.
>>> virus = organizmi.Organizmi('virus')
Stvorili smo organizam koji se zove virus
>>> konj = organizmi.Organizmi('konj')
Stvorili smo organizam koji se zove konj
>>> organizmi.Organizmi.koliko()
Trenutno je 3 organizama na planeti.
>>> del konj
konj je parazit.
Sada je uništen.
Na našoj veseloj planeti živi 2 životnih formi.
Planeta je funkcionalna
>>> del virus
virus je parazit.
Sada je uništen.
Na našoj veseloj planeti živi 1 životnih formi.
Planeta je funkcionalna
>>> del ameba
ameba je parazit.
Sada je uništen.
Ups...ameba je poslednja životna vrsta.
Planeta je uspešno uništena.
>>>
Čekaj malo, gde žuriš – šta je ovo @classmethod?
Ne boj se, nisam mislio ovo da preskočim. Kao što polja mogu da budu dve vrste – isto tako i metode mogu da budu dve vrste – funkcionalnosti koje pripadaju objektu ili funkcionalnosti koje pripadaju klasi. Ova funkcija je ovde da bi smo mi od klase mogli da dobijemo odgovor – koliko je objekata stvorila.
Prma tome, metod koliko() je zapravo funkcionalnost koja pripada klasi Organizmi, a ne nekom objektu te klase. Mi to postižemo tako što ovakve metode definišemo uz reči classmethod ili staticmethod, što zavisi od potrebe da li želimo da znamo kojoj klasi pripada. Budući da je ovo ipak metoda koja radi sa varijablom klase mi možemo da koristimo oznaku classmethod.
Oznake @staticmethod ili @classmethod koje se nalaze odmah ispred funkcije za koju želimo da bude metoda klase se nazivaju dekorateri. O njihovoj upotrebi možeš više da saznaš na ovoj internet adresi.
Dekoratere možeš da zamisliš kao neku prečicu koja služi za pozivanje neke direktne komande, kao što smo videli u ovom primeru metode klase, koji smo drugačije mogli napisati:
def koliko():
'''Koliko imamo organizama.'''
print('Trenutno je {:d} organizama na planeti.'.format(Organizmi.ukupno))
koliko = classmethod(koliko)
U ovom našem malom modulu, mi smo koristili i docstring za našu klasu i definisane metode. Sada im mi možemo pristupiti vrlo lako!
>>> organizmi.Organizmi.__doc__
'Klasa koja predstavlja sve životne vrste na zemlji.'
>>> organizmi.Organizmi.koliko.__doc__
'Koliko imamo organizama.'
Ali, najprijatnije iznenađenje te čeka kada probaš:
>>> help(organizmi)
Ukoliko dolaziš sa nekim programerskim predznanjem znaj da su svi članovi ove klase javni. U Pythonu ne postoje privatne varijable ali postoji jedan „trik“: ako za imena polja koristiš oznake koje počinju sa dvostrukim donjim crtama (na primer __privatnavar) , Python će takvu varijablu da prepozna da ona za tebe ima neko specijalno značenje, pa će da je „sakrije“ (zapamti – dvostruka donja crta na početku imena znači skrivanje imena) pa je efekat sličan kao da je to privatna promenljiva.
Zbog toga, usvojeni standard je da se za svaku promenljivu koja se koristi samo u okviru klase ili objekta i koja je potrebna samo toj klasi ili objektu, dodeljuju imena koja počinju sa donjom crtom, a da svi ostali nazivi budu javni, kako bi mogli da se koriste kako iz našeg programa, tako i od strane nekih drugih klasa/objekata. Zapamti – ovo je samo konvencija, a ne neka osobina koja je implementirana u sam Python.
14.4 Metoda __init__ | Indeks | 14.6 Inheritance |
Коментари
Постави коментар