När kan/ska man använda arv?
Arv används när flera klasser har gemensamma grund och är sedan specialiserade i någon grad. T.ex. så har bilar och motorcyklar likheter och skillnader, båda är fordon men de har olika egenskaper. Då kan man ha en basklass som heter Fordon och två subklass en för Bil och en för Motorcykel. Vi lägger funktionalitet som fler objekt har gemensamt i basklassen, och sen specialiserar vi de olika egenskaperna i subklasser.
Arv används för att återanvända kod och göra koden mer dynamiskt.
Vi tänker oss att vi har ett system med olika typer av videor, vi behöver spara information om filmer och videoklipp (t.ex. youtube videor). De har saker som är gemensamt t.ex. de har en längd och titel. Filmer däremot brukar också ha en regisör och en rating. De sakerna brukar inte videoklip ha. Däremot har de gilla, ogilla och kommentarer. Detta kan vi bygga upp med arv genom att skapa tre klasser. En basklass med den gemensamma informationen (längd, titel) och en subklass för vardera specialisering.
Nedanför kan ni se hur relationen ser ut i ett klassdiagram.
#Skapa arv mellan klasser
Vi använder Video som bas/förälder klass och skapar subklassen Movie.
Vi börjar med att skapa bas/förälder klassen Video. Alla typer av videor har en titel och en spellängd, vi skapar instansattribut för det:
class Video():
def __init__(self, title, length):
self.title = title
self.length = length
def print_info(self):
print(
(
"{title} is {length} minute(s) long,"
"has the director {dir} and a rating of {rating}"
).format(
title=self.title,
length=self.length,
dir=self.director,
rating=self.rating
))
Vi har en simple klass med två instansattribut och en instansmetod för att skriva ut instansattributen. Vi skapar subklassen Movie. Till skillnad från en Video har en Movie även en director och en rating.
#Subklassen Movie
Arvs relationen skapas genom att man skriver klassen man vill ärva från inom parenteserna efter klassens namn i klassdeklarationen.
class Movie(Video):
Nu har vi påbörjat klassen Movie och bestämt att den ärver från Video. Vi lägger till en konstruktor i Movie där vi ser till att instansattributen från Video följer med i Movie:
class Movie(Video):
def __init__(self, title, length, director, rating):
super().__init__(title, length)
self.director = director
self.rating = rating
Metoden super()
returnerar basklassen, Video, och då kan vi anropa Video’s konstruktor med .__init__()
(detta är ett av få gånger då vi själva anropar en magisk metod). Det är denna raden som gör att Movie får tillgång till metoder och attribut från Video. Vi skickar med title
och length
till konstruktorn för Video och då läggs de attributen, med rätt värden, till i Movie objekt som skapas. Sen lägger vi till de specifika attributen director
och rating
på objekten.
Nu kan vi initiera ett Movie objekt och ge de ett namn utan att ha skapat ett title och length attribut i Movie klassen.
Vi kan även anropa funktionen print_info
både från ett Movie och ett Video objekt.
>>> home = Video("Home video 2", 5)
>>> dogs = Movie("Isle of Dogs", 101, "Wes Anderson", 8.0)
>>> home.print_info()
Home video 2 is 5 minutes long
>>> dogs.print_info()
Isle of Dogs is 101 minutes long
Vi har en Bas och en Subklass, men det som skiljer subklassen från basklassen visas inte på något sätt. Vi vill få med director och rating när det är ett Movie objekt som skriver ut sin info. Vi testar att lägga till de båda attributen från Movie i print_info
metoden i Video.
class Video():
def __init__(self, title, length):
self.title = title
self.length = length
def print_info(self):
print("{title} is {length} minute(s) long, has the director {dir} and a rating of {rating}".format(
title=self.title,
length=self.length,
dir=self.director,
rating=self.rating
))
...
>>> home.print_info()
Traceback:
AttributeError: 'Video' object has no attribute 'director'
>>> dogs.print_info()
Isle of Dogs is 101 minute(s) long, has the director Wes Anderson and a rating of 8.0
Metoden fungerar när den anropas från ett Movie objekt men kraschar när en Video instans används. Som felmeddelandet säger så saknas attributet director
i Video. Det är bara Movie som har det. Återställ metoden så den ser ut som tidigare.
I en subklass kommer vi åt alla attribut och metoder som finns i en basklass med ett undantag, som vi kommer gå igenom längre fram i guiden, men i en basklass kommer vi inte åt något från subklassen. I nästa steg i guiden kollar vi på hur man löser ovanstående problem.
Det går att ärva i flera steg. T.ex. klassen TvSerie kan ärva från Movie som ärver av Video. Då har vi tillgång till attribut och metoder både från Movie och Video. En klass kan även ärva från flera klasser samtidigt, Movie kan t.ex. ärva från Video och Animation samtidigt. Det kallas för multipelt arv.
Vi fortsätter med att skapa den andra subklassen.
#Subklassen WebVideo
Vi skapar WebVideo
på samma sätt som Movie
. Fast ger den attributen, likes
, dislikes
och comments
istället.
class WebVideo(Video):
def __init__(self, title, length, likes, dislikes, comments):
super().__init__(title, length)
self.likes = likes
self.dislikes = dislikes
self.comments = comments
Skapa ett WebVideo objekt också och testa använda print metoden.
>>> home = Video("Home video 2", 5)
>>> dogs = Movie("Isle of Dogs", 101, "Wes Anderson", 8.0)
>>> charlie = WebVideo("Charlie bit my finger", 1, 100000, 0, ["What a funny clip", "Amazing"])
>>> home.print_info()
Home video 2 is 5 minutes long
>>> dogs.print_info()
Isle of Dogs is 101 minutes long
>>> charlie.print_info()
Charlie bit my finger is 1 minutes long
Precis som för Movie, kommer vi åt attribut och metoder från Video i WebVideo. Movie och WebVideo har ingen relation till varandra, det går inte att komma åt likes
från ett Movie objekt t.ex.
I nästa steg löser vi att print_info
inkluderar datan från subklasserna.
#Revision history
- 2022-01-19: (B, aar) La till subklassen WebVideo.
- 2018-11-26: (A, aar) Första versionen, uppdelad av större dokument.