Dokumentasjon#

God, gjenbrukbar kode bør ha god dokumentasjon. Å skrive dokumentasjon gjør at andre kan bruke koden vår uten å måtte lese hele kildekoden.

Type hints#

I funksjoner og metoder trenger vi ikke å definere hvilken type data vi forventer som argument, og heller ikke hvilken type data som funksjonen returnerer. Det er vi nødt til i andre språk som Java. La oss se på et eksempel.

class Elev:
    def __init__(self, navn):
        self.navn = navn
      
    def hils(self, kompis_navn):
        return f"{self.navn} hilser på {kompis_navn}"

elev = Elev("Linnea")
print(elev.hils("Beate"))
Linnea hilser på Beate

hils()-metoden skal ta en string kompis_navn og returnere en string f{self.navn} hilser {kompis_navn}, men det er ingenting som hindrer oss i å skrive inn feil datatyper og det er heller ingenting som sier oss at metoden skal returnere en string, med mindre vi leser koden.

For å signalisere hvilke datatyper vi forventer som argument, og hvilke datatyper som returneres, kan vi bruke type hints.

class Elev:
    def __init__(self, navn):
        self.navn = navn
    
    def hils(self, kompis_navn : str) -> str:
        return f"{self.navn} hilser på {kompis_navn}"

Der det står kompis_navn : str mener vi altså at variabelen med navn kompis_navn burde være en str. Pilen -> str betyr at funksjonen skal returnere en str. Dette sier til brukeren hva som returneres og hva som skal puttes inn som argument. Dette vises for eksempel i VSCode og dokumentasjonen vi lager senere.

Lage og bruke programmer i andre programmer#

Det er enkelt å lage og bruke programmer i andre programmer. Vi lager først en Python-fil som definerer funksjoner og klasser, og deretter bruker vi import for å hente inn klassene og funksjonene til et annet program.

La oss lage et program som regner på sirkler.

# sirkelregner.py

import math

class Sirkel:
    # Konstruktøren
    def __init__(self, r : float):
        self.r = r
        self.areal = math.pi * self.r ** 2
        self.omkrets = 2 * math.pi * self.r
    
    # Skalerer sirkelen med en faktor k
    def skaler(self, k : float):
        self.__init__(self.r * k)

    # Returnerer en string når man printer objekter av klassen
    def __str__(self) -> str:
        return f"Sirkel med radius: {self.r}, areal: {round(self.areal, 2)} og omkrets: {round(self.omkrets, 2)}"

Hvis vi lagrer koden over som sirkelregner.py kan vi enkelt bruke import for å hente innholdet til et annet program. Dette kan vi gjøre på noen forskjellige måter:

  • Vi kan bruke import sirkelregner og deretter bruke sirkelregner.Sirkel().

  • Vi kan bruke import sirkelregner as sr og deretter bruke sr.Sirkel().

  • Vi kan bruke from sirkelregner import Sirkel og deretter bruke Sirkel().

Siden pakken bare inneholder én klasse velger jeg å bruke siste alternativ.

from sirkelregner import Sirkel

sirkel = Sirkel(1) # Lager et Sirkel-objekt
print(sirkel) # Skriver ut Sirkel-objektet
sirkel.skaler(2) # Skalerer Sirkel-objektet med faktor 2
print(sirkel) # Skriver ut Sirkel-objektet
Sirkel med radius: 1.000, areal: 3.142 og omkrets: 6.283
Sirkel med radius: 2.000, areal: 12.566 og omkrets: 12.566

Som vi ser kan vi enkelt importere Sirkel-klassen og bruke den i et annet program. Det avhenger så klart av at vi vet hvordan vi skal bruke sirkelregner. Hvis vi skal formidle hvordan en pakke fungerer, må vi skrive en god dokumentasjon.

Dokumentasjon med docstrings#

Vi kan enkelt skrive dokumentasjonen inn i koden ved å bruke """-kommentarer. Dette er en standard som er støttet av Python. Akkurat hvordan man formaterer setningene i kommentarene er det flere ulike konvensjoner for. I dette eksempelet vil vi bruke konvensjonen Google Docstrings.

Her kommer et eksempel av sirkelregner.py skrevet med docstrings. Legg merke til hvordan man kan spesifisere attributter, args (argumenter) og return-verdier.

"""Dette programmet gir en klasse som gjør at man kan lage et skalerbart sirkelobjekt.

Typisk eksempel på bruk:

    sirkel = Sirkel(1)
    print(sirkel)
    sirkel.skaler(2)
    print(sirkel)
"""

import math

class Sirkel:
    """En klasse som håndterer sirkelobjekter
    
    Attributes:
        r: en radius som et tall.
        areal: arealet regnet fra radius
        omkrets: omkretsen regnet fra radius
    """

    # Konstruktøren
    def __init__(self, r : float):
        """Initialiserer et sirkelobjekt med en radius r.
        
        Args:
            r: en radius som et tall
        """
        self.r = r
        self.areal = math.pi * self.r ** 2
        self.omkrets = 2 * math.pi * self.r
    
    # Skalerer sirkelen med en faktor k
    def skaler(self, k : float):
        """Skalerer sirkelobjektet med en faktor k.
        
        Args:
            k: en faktor (tall) som skal ganges med radius.
        """
        self.__init__(self.r * k)

    # Returnerer en string når man printer objekter av klassen
    def __str__(self) -> str:
        """Gir en string når man printer sirkelobjekter.
        
        Returns:
            En string som inneholder radius, areal og omkrets av sirkelen.    
        """
        return f"Sirkel med radius: {self.r}, areal: {round(self.areal, 2)} og omkrets: {round(self.omkrets, 2)}"

Vi kan nå enkelt hente hele dokumentasjonen til dette programmet ved å bruke help()-funksjonen i Python.

import sirkelregner
help(sirkelregner)               # Gir hele dokumentasjonen
help(sirkelregner.Sirkel)        # Gir dokumentasjonen for Sirkel-klassen
help(sirkelregner.Sirkel.skaler) # Gir dokumentasjonen for skaler()-metoden

Generere dokumentasjon med pydoc#

Python-installasjoner kommer med pakken pydoc som lar oss skrive dokumentasjonen fra docstrings over til en .html-fil som kan åpnes i nettleseren. Dette gjør vi ved å sørge for at terminalvinduet er åpnet i mappen som man har pakken og skrive

python -m pydoc -w sirkelregner

Erstatt sirkelregner med din pakkes navn. Da vil du få en .html-fil med litt retro stil, men som forklarer metoder og objekter i programmet ditt, og som kan sendes videre til brukeren.