Näkyvyysmääreet
Aikaisemmin mainittiin, että yliluokassa yksityiseksi määritettyihin piirteiisin ei pääse käsiksi aliluokassa. Tarkastellaan esimerkkinä luokkaa Muistikirja
, jossa muistiinpanojen säilyttämiseen käytettävä lista-attribuutti on piilotettu asiakkailta:
class Muistikirja:
""" Muistikirjaan voidaan tallentaa muistiinpanoja merkkijonoina """
def __init__(self):
# yksityinen attribuutti
self.__muistiinpanot = []
def lisaa_muistiinpano(self, muistiinpano):
self.__muistiinpanot.append(muistiinpano)
def palauta_muistiinpano(self, indeksi):
return self.__muistiinpanot[indeksi]
def kaikki_muistiinpanot(self):
return ",".join(self.__muistiinpanot)
Luokan sisäisen eheyden kannalta tietorakenteena toimivan listan piilottaminen asiakkaalta on sinänsä järkevää, koska luokka tarjoaa itse sopivat operaatiot muistiinpanojen lisäämiseksi ja selaamiseksi. Ongelmalliseksi tilanne muodostuu, jos yritetään kirjoittaa Muistikirja
-luokan perivät luokka ProMuistikirja
, johon halutaan lisätä muistiinpanojen etsiminen ja järjestäminen. Piilotettu attribuutti ei ole käytettävissä myöskään aliluokissa; metodi etsi_muistiinpanot
antaa kutsuttaessa virheen:
class MuistikirjaPro(Muistikirja):
""" Parempi muistikirja haku- ja järjestystoiminnoilla """
def __init__(self):
# Tämä on ok, koska luokan Muistikirja konstruktori
# on julkinen
super().__init__()
# Tämä antaa virheen
def etsi_muistiinpanot(self, hakusana):
loydetty = []
# Attribuutti __muistiinpanot on yksityinen, eikä näy
# aliluokalle
for muistiinpano in self.__muistiinpanot:
if hakusana in muistiinpano:
loydetty.append(muistiinpano)
return loydetty
AttributeError: 'MuistikirjaPro' object has no attribute '_MuistikirjaPro__muistiinpanot'
Suojatut piirteet
Toisin kuin joistain muista ohjelmointikielistä, Pythonista ei suoraan löydy ominaisuutta joka piilottaa piirteet asiakkailta mutta samaan aikaan avaa ne mahdollisille aliluokille. Ratkaisuksi Python-yhteisö onkin päätynyt konventioon eli yleisesti ymmärrettyyn merkintätapaan suojatuille (eli protected) piirteille.
Koska piirre voidaan piilottaa kirjoittamalla sen tunnisteen (eli nimen) eteen kaksi alaviivaa
def __init__(self):
self.__muistiinpanot = []
on yleisesti sovittu että yhdellä alaviivalla alkavat piirteet ovat tarkoitettu ainoastaan luokan ja sen aliluokkien käyttöön, eikä niitä tulisi käyttää suoraan sen ulkopuolelta.
def __init__(self):
self._muistiinpanot = []
Alla on esitetty koko muistikirjaesimerkki uudestaan niin, että muistiinpanot on merkitty suojatuiksi yliluokassa yksityisen sijasta:
class Muistikirja:
""" Muistikirjaan voidaan tallentaa muistiinpanoja merkkijonoina """
def __init__(self):
# suojattu attribuutti
self._muistiinpanot = []
def lisaa_muistiinpano(self, muistiinpano):
self._muistiinpanot.append(muistiinpano)
def palauta_muistiinpano(self, indeksi):
return self._muistiinpanot[indeksi]
def kaikki_muistiinpanot(self):
return ",".join(self._muistiinpanot)
class MuistikirjaPro(Muistikirja):
""" Parempi muistikirja haku- ja järjestystoiminnoilla """
def __init__(self):
# Tämä on ok, koska luokan Muistikirja konstruktori
# on julkinen
super().__init__()
# Nyt metodi toimii, koska suojattu attribuutti näkyy
# aliluokalle
def etsi_muistiinpanot(self, hakusana):
loydetty = []
for muistiinpano in self._muistiinpanot:
if hakusana in muistiinpano:
loydetty.append(muistiinpano)
return loydetty
Seuraavassa taulukossa on vielä esitetty piirteiden näkyvyys kaikkien eri suojausmääreiden tapauksessa:
Näkyvyysmääre | Esimerkki | Näkyy asiakkaalle | Näkyy aliluokalle |
---|---|---|---|
Julkinen | self.nimi | kyllä | kyllä |
Suojattu | self._nimi | ei | kyllä |
Yksityinen | self.__nimi | ei | ei |
Näkyvyysmääreet toimivat vastaavasti kaikkien piirteiden kanssa. Luokassa Henkilo oleva metodi isot_alkukirjaimet
on suojattu, joten sitä voi käyttää myös aliluokassa Jalkapalloilija:
class Henkilo:
def __init__(self, nimi: str):
self._nimi = self._isot_alkukirjaimet(nimi)
def _isot_alkukirjaimet(self, nimi):
nimi_isoilla = []
for n in nimi.split(" "):
nimi_isoilla.append(n.capitalize())
return " ".join(nimi_isoilla)
def __repr__(self):
return self.__nimi
class Jalkapalloilija(Henkilo):
def __init__(self, nimi: str, lempinimi: str, pelipaikka: str):
super().__init__(nimi)
# metodia voi kutsua, koska se on suojattu yliluokassa
self.__lempinimi = self._isot_alkukirjaimet(lempinimi)
self.__pelipaikka = pelipaikka
def __repr__(self):
r = f"Jalkapalloilija - nimi:{self._nimi}, lempinimi: {self.__lempinimi}"
r += f", pelipaikka: {self.__pelipaikka}"
return r
# Testataan
if __name__ == "__main__":
jp = Jalkapalloilija("petri pythonen", "pyttis", "hyökkääjä")
print(jp)
Jalkapalloilija - nimi:Petri Pythonen, lempinimi: Pyttis, pelipaikka: hyökkääjä