Åtkomstmodifierare
Om en egenskap definieras som privat i basklassen är den inte direkt åtkomlig i några härledda klasser, liksom kort nämndes i föregående avsnitt. Låt oss ta en titt på ett exempel. I klassen Anteckningsbok
nedan lagras anteckningarna i en lista, och listattributet är privat:
class Anteckningsbok:
""" En Anteckningsbok förvarar anteckningar i strängformat """
def __init__(self):
# privat attribut
self.__anteckningar = []
def tillsatt_anteckning(self, anteckning):
self.__anteckningar.append(anteckning)
def hamta_anteckning(self, index):
return self.__anteckningar[index]
def alla_anteckningar(self):
return ",".join(self.__anteckningar)
Om klassens integritet är viktig är det vettigt att göra listattributen anteckningar
privat. Klassen förser trots allt klienten med lämpliga metoder för att lägga till och bläddra i anteckningar. Detta tillvägagångssätt blir problematiskt om vi definierar en ny klass AnteckningsbokPro
, som ärver Anteckningsbok
-klassen. Det privata listattributet är inte tillgängligt för klienten, men det är inte heller tillgängligt för de härledda klasserna. Om vi försöker komma åt det, som i metoden hamta_anteckningar
nedan, får vi ett felmeddelande:
class AnteckningsbokPro(Anteckningsbok):
""" En bättre Anteckningsbok med sökfunktionalitet """
def __init__(self):
# Detta är ok, eftersom konstruktorn är offentlig trots understrykning
super().__init__()
# Detta orsakar ett fel
def hitta_anteckningar(self, sokord):
hittade = []
# Attributet __anteckningar är privat, den härledda
# klassen kan inte komma åt den direkt
for anteckning in self.__anteckningar:
if sokord in anteckning:
hittade.append(anteckning)
return hittade
AttributeError: 'AnteckningsbokPro' object has no attribute '_AnteckningsbokPro__anteckningar'
Skyddade egenskaper
Många objektorienterade programmeringsspråk har en funktion, oftast ett speciellt nyckelord, för att skydda egenskaper. Detta innebär att en egenskap ska vara dold för klassens klienter, men hållas tillgänglig för dess underklasser. Python avskyr i allmänhet nyckelord, så ingen sådan funktion är direkt tillgänglig i Python. Istället finns det en konvention för att markera skyddade egenskaper på ett visst sätt.
Kom ihåg att en egenskap kan döljas genom att prefixera dess namn med två understreck:
def __init__(self):
self.__anteckningar = []
Den överenskomna konventionen för att skydda en egenskap är att prefixera namnet med endast ett understreck. Nu är detta bara en konvention. Ingenting hindrar en programmerare från att bryta mot konventionen, men det anses vara en dålig programmeringspraxis.
def __init__(self):
self._anteckningar = []
Nedan har vi hela Anteckningsbok-exemplet, med skyddade _anteckningar
istället för privata __anteckningar
:
class Anteckningsbok:
""" En Anteckningsbok förvarar anteckningar i strängformat """
def __init__(self):
# Skyddade attribut
self._anteckningar = []
def tillsatt_anteckning(self, anteckning):
self._anteckningar.append(anteckning)
def hamta_anteckning(self, index):
return self._anteckningar[index]
def alla_anteckningar(self):
return ",".join(self._anteckningar)
class AnteckningsbokPro(Anteckningsbok):
""" En bättre Anteckningsbok med sökfunktionalitet """
def __init__(self):
# Detta är ok, eftersom konstruktorn är offentlig trots understrykning
super().__init__()
# Nu fungerar metoden, eftersom den skyddadde attributen är
# ankomstbar till den härledda klassen
def hitta_anteckningar(self, sokord):
hittade = []
for anteckning in self._anteckningar:
if sokord in anteckning:
hittade.append(anteckning)
return hittade
Nedan har vi en praktisk tabell för synligheten av attribut med olika åtkomstmodifierare:
Åtkomstmodifierare | Exempel | Synlig till klienten | Synlig till härledd klass |
---|---|---|---|
Offentlig | self.namn | ja | ja |
Skyddad | self._namn | nej | ja |
Privat | self.__namn | nej | nej |
Åtkomstmodifierare fungerar på samma sätt med alla egenskaper. I klassen Person
nedan har vi till exempel den skyddade metoden versalisera_initialer
Den kan användas från den härledda klassen Fotbollsspelare
:
class Person:
def __init__(self, namn: str):
self._namn = self._versalisera_initialer(namn)
def _versalisera_initialer(self, namn):
namn_versaliserat = []
for n in namn.split(" "):
namn_versaliserat.append(n.capitalize())
return " ".join(namn_versaliserat)
def __repr__(self):
return self.__namn
class Fotbollsspelare(Person):
def __init__(self, namn: str, smeknamn: str, position: str):
super().__init__(namn)
# metoden är ankomstbar eftersom den är skyddad i basklassen
self.__smeknamn = self._versalisera_initialer(smeknamn)
self.__position = position
def __repr__(self):
r = f"Fotbollsspelare - namn:{self._namn}, smeknamn: {self.__smeknamn}"
r += f", position: {self.__position}"
return r
# Testar klasserna
if __name__ == "__main__":
fs = Fotbollsspelare("peter pythonson", "putte", "anfallare")
print(fs)
Fotbollsspelare - namn:Peter Pythonson, smeknamn: Putte, position: anfallare
Se dina poäng genom att klicka på cirkeln nere till höger av sidan.