Osa 9

Fler exempel med klasser

Följande exempel består av två klasser. Klassen Punkt är en modell för en punkt i ett tvådimensionellt rum. Klassen Stracka är en modell för ett linjesegment mellan två punkter. Koden nedan är kommenterad, läs gärna kommentarerna för att förstå hur klasserna fungerar.

import math

class Punkt:
    """ Klassen representerar en punkt i ett tvådimensionellt rum """

    def __init__(self, x: float, y: float):
        # Attributen är offentliga, eftersom vilket värde som helst kan användas som värde för x och y.
        self.x = x
        self.y = y

    # Denna klassmetod returnerar en ny punkt vid origo (0, 0)
    # Det är möjligt att returnera en ny instans av klassen inifrån klassen
    @classmethod
    def origo(cls):
        return Punkt(0, 0)

    # Klassmetoden skapar en ny punkt baserad på den givna punkten
    # Den nya punkten är en spegelbild av den givna punkten på en eller båda axlarna.
    # Till exempel är punkten (1, 3) speglad på x-axeln (1, -3)
    @classmethod
    def spegelbild(cls, punkt, spegla_x: bool, spegla_y: bool):
        x = punkt.x
        y = punkt.y
        if spegla_x:
            y = -y
        if spegla_y:
            x = -x

        return Punkt(x, y)

    def __str__(self):
        return f"({self.x}, {self.y})"

class Stracka:
    """ Klassen modellerar en sträcka i ett tvådimensionellt rum """

    def __init__(self, borjan: Punkt, slut: Punkt):
        # Dessa attribut är offentliga eftersom två valfria punkter kan accepteras
        self.borjan = borjan
        self.slut = slut

    # Denna metod använder Pythagoras sats för att beräkna längden på sträckan
    def langd(self):
        summa = (self.slut.x - self.borjan.x) ** 2 + (self.slut.y - self.borjan.y) ** 2
        return math.sqrt(summa)

    # Metoden returnerar mitten av sträckan
    def medelpunkt(self):
        medelx = (self.borjan.x + self.slut.x) / 2
        medely = (self.borjan.y + self.slut.y) / 2
        return Punkt(medelx, medely)

    def __str__(self):
        return f"{self.borjan} ... {self.slut}"
punkt = Punkt(1,3)
print(punkt)

origo = Punkt.origo()
print(origo)

punkt2 = Punkt.spegelbild(punkt, True, True)
print(punkt2)

stracka = Stracka(punkt, punkt2)
print(stracka.langd())
print(stracka.medelpunkt())
print(stracka)
Exempelutskrift

(1, 3) (0, 0) (-1, -3) 6.324555320336759 (0.0, 0.0) (1, 3) ... (-1, -3)

Standardvärden för parametrar

I Python-programmering kan du i allmänhet ange ett standardvärde för alla parametrar. Standardvärden kan användas i både funktioner och metoder.

Om en parameter har ett standardvärde behöver du inte inkludera ett värde som ett argument när du anropar funktionen. Om ett argument anges ignoreras standardvärdet. Om inte, används standardvärdet.

Default-värden används ofta i konstruktörer. Om man kan förvänta sig att all information inte är tillgänglig när ett objekt skapas är det bättre att inkludera ett standardvärde i definitionen av konstruktörsmetoden än att tvinga klienten att ta hand om problemet. Detta gör det enklare att använda klassen ur klientens synvinkel, men det säkerställer också objektets integritet. Med ett fastställt standardvärde kan vi t.ex. vara säkra på att ett "tomt" värde alltid är detsamma, såvida inte klienten specifikt vill ange något annat. Om ett standardvärde inte anges är det upp till kunden att tillhandahålla ett "tomt" värde. Det kan t.ex. vara en tom sträng "", det speciella tomma objektet None eller strängen "inte angivet".

Låt oss ta en titt på ännu en klass som representerar en studerande. När ett nytt Studerande-objekt skapas måste klienten ange ett namn och ett studerandenummer. Studerandenumret är privat och ska inte ändras i efterhand. Dessutom har ett Studerande-objekt attribut för studiepoäng och anteckningar, vilka har standardvärden som anges i konstruktorn. Nya värden kan skickas som argument till konstruktören, men de kan också utelämnas så att standardvärdena används istället. Titta gärna på kommentarerna i koden för att bättre förstå vad varje metod gör.

class Studerande:
    """ Modellerar en endaste studerande """

    def __init__(self, namn: str, studerandenummer: str, studiepoang:int = 0, anteckningar:str = ""):
        # Kallar sättar-metoden
        self.namn = namn

        if len(studerandenummer) < 5:
            raise ValueError("Studerandenumret ska ha minst 5 tecken")

        self.__studerandenummer = studerandenummer

        # Kallar sättar-metoden
        self.studiepoang = studiepoang

        self.__anteckningar = anteckningar

    @property
    def namn(self):
        return self.__namn

    @namn.setter
    def namn(self, namn):
        if namn != "":
            self.__namn = namn
        else:
            raise ValueError("Namnet kan inte vara tomt")

    @property
    def studerandenummer(self):
        return self.__studerandenummer

    @property
    def studiepoang(self):
        return self.__studiepoang

    @studiepoang.setter
    def studiepoang(self, sp):
        if sp >= 0:
            self.__studiepoang = sp
        else:
            raise ValueError("Studiepoäng kan inte vara ett negativt tal")

    @property
    def anteckningar(self):
        return self.__anteckningar

    @anteckningar.setter
    def anteckningar(self, anteckningar):
        self.__anteckningar = anteckningar

    def sammanfattning(self):
        print(f"Studerande {self.__namn} ({self.studerandenummer}):")
        print(f"- studiepoäng {self.__studiepoang}")
        print(f"- anteckningar: {self.anteckningar}")
# Skickar endast namnet och studerandenumret
studerande1 = Studerande("Sam Studerande", "12345")
studerande1.sammanfattning()

# Skickar namnet, studerandenummer och studiepoäng
studerande2 = Studerande("Saul Studerande", "54321", 25)
studerande2.sammanfattning()

# Skickar alla uppgifter
studerande3 = Studerande("Sara Studerande", "99999", 140, "tillägstid i tenter")
studerande3.sammanfattning()

# Skickar anteckningar, men inte studiepoäng
# Obs: parametern måste nu bli namngiven när argumenten inte är i ordning
studerande4 = Studerande("Saga Studerande", "98765", anteckningar="avlägsen studieår 20-21")
studerande4.sammanfattning()
Exempelutskrift

Studerande Sam Studerande (12345):

  • studiepoäng 0
  • anteckningar:

Studerande Saul Studerande (54321):

  • studiepoäng 25
  • anteckningar:

Studerande Sara Studerande (99999):

  • studiepoäng 140
  • anteckningar: tillägstid i tenter

Studerande Saga Studerande (98765):

  • studiepoäng 0
  • anteckningar: avlägsen studieår 20-21

OBS: Det finns ingen sättar-metod för attributet studerande_nummer eftersom det inte är meningen att studerandenumret ska ändras.

Det finns en ganska betydande hake när man använder standardvärden för parametrar. Följande exempel som modellerar ännu en typ av studerande kommer att belysa detta mer:

class Studerande:
    def __init__(self, namn, gjorda_kurser=[]):
        self.namn = namn
        self.gjorda_kurser = gjorda_kurser

    def tillsatt_prestation(self, kurs):
        self.gjorda_kurser.append(kurs)
studerande1 = Studerande("Sam Studerande")
studerande2 = Studerande("Saul Studerande")

studerande1.tillsatt_prestation("ItP")
studerande1.tillsatt_prestation("Tira")

print(studerande1.gjorda_kurser)
print(studerande2.gjorda_kurser)
Exempelutskrift

['ItP', 'Tira'] ['ItP', 'Tira']

Om du lägger till slutförda kurser i Sams lista läggs dessa kurser också till i Sauls lista. Faktum är att dessa två är exakt samma lista, eftersom Python återanvänder referensen som lagras i standardvärdet. Att skapa de två nya Studerande-objekten i exemplet ovan är likvärdigt med följande:

kurser = []
studerande1 = Studerande("Sam Studerande", kurser)
studerande2 = Studerande("Saul Studerande", kurser)

Standardvärdena för parametrar bör aldrig vara instanser av mer komplicerade, föränderliga datastrukturer, t.ex. listor. Problemet kan kringgås genom att göra följande ändringar i konstruktorn för klassen Studerande:

class Studerande:
    def __init__(self, namn, gjorda_kurser=None):
        self.namn = namn
        if gjorda_kurser is None:
            self.gjorda_kurser = []
        else:
            self.gjorda_kurser = gjorda_kurser

    def tillsatt_prestation(self, kurs):
        self.gjorda_kurser.append(kurs)
studerande1 = Studerande("Sam Studerande")
studerande2 = Studerande("Saul Studerande")

studerande1.tillsatt_prestation("ItP")
studerande1.tillsatt_prestation("Tira")

print(studerande1.gjorda_kurser)
print(studerande2.gjorda_kurser)
Exempelutskrift

['ItP', 'Tira'] []

Den stora finalen

Fastän följande övning avslutar den här delen av materialet, så har de tekniker som krävs för att lösa den redan behandlats i avsnittet som heter Objekt som attribut. Du behöver inte använda @property-dekoratorn eller standardvärden för parametrar i den här övningen. Den här övningen är mycket lik övningarna "en presentask" och "den kortaste personen i rummet".

Loading

Svara avslutningsvis på följande frågeformulär:

Loading...
:
Loading...

Log in to view the quiz

Du har nått slutet av den här delen!

Se dina poäng genom att klicka på cirkeln nere till höger av sidan.