Göm menyn

Programmeringspraxis

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

- Antoine de Saint-Exupery

Detta kapitel beskriver inte hur du skriver kod för att datorn ska förstå den, utan hur du skriver kod så att du och andra människor förstår den.

Som utvecklare så är det sällan mängden kod eller antalet funktioner som utmärker hur duktig man är. Det som särskiljer en duktig programmerare är hur bra hen är på att följa olika programmeringspraxis; goda koncept som bör eftersträvas. Programmeringspraxis som bör följas av pythonutvecklare står definierade i vissa PEP-dokument (Python Enhancement Proposals).

Korrekthet

För att avgöra om en funktion beter sig korrekt måste vi fastställa vad det är den ska göra. Funktionens kontrakt specificerar exakt hur funktionen ska bete sig.

Antingen skriver du kontraktet efter att du skrivit en funktion för att försäkra dig om att funktionen behåller det nuvarande beteendet, alternativt behöver du skriva eller redigera en funktion med fastställt kontrakt. Det är viktigt att funktionen uppfyller exakt den funktionalitet som beskrivs i kontraktet. För att vara säker på att kontraktet uppfylls används tester.

Kontraktet finns definierat i funktionens docstring (se senare avsnitt)

Läsbarhet

En sak att ha i åtanke när du skriver kod är att den kommer att läsas fler gånger än vad den kommer att skrivas. Det är därför viktigt att se till att andra, och även du själv, kan förstå koden vid ett senare skede och göra nödvändiga ändringar.

För att Python-kod ska vara konsekvent finns ett antal riktlinjer som bör följas.

Självdokumenterande kod

Kod som är självförklarande nog att ingen extra dokumentation behövs för att den ska förstås kallas självdokumenterande och är något programmerare bör sträva efter. Detta uppnås främst med hjälp av beskrivande variabel- och funktionsnamn samt väl strukturerad kod.

Några exempel på funktionsnamn: has_been_read, contains_key, is_substring.

Namnformatering

Namn ska formateras enligt vissa riktlinjer. Det är viktigt att dessa riktlinjer följs då det annars blir svårt för andra att arbeta med din kod. Python i sig tar inte hänsyn till vad du använder. En generell regel är att ny kod ska namnges på samma sätt som existerande kod.

Olika språk har olika standarder men för Python gäller följande:

CamelCase

  • Namnet kommer från pucklarna som texten får.
  • Alla ord skrivs ihop där varje ord börjar med stor bokstav.
  • Används för klasser och undantag i Python.
  • Alternativt kan första ordet ha liten bokstav, detta används ej i Python.

snake_case

  • Namnet kommer från de långsmala namn som bildas.
  • Alltid små bokstäver med ord separerade med understreck.
  • Används för variabler, funktioner, metoder och moduler (modulnamn bör endast vara ett ord eller ihopskrivet utan understreck)

CAPITAL_LETTERS

  • Används för variabler vars värde ska vara konstant (så kallade konstanter).

Blankrader

Alla funktioner och klasser på toppnivå (dvs ej indenterade) ska omringas av två blankrader. Undantag kan göras för enradade funktioner. Inre funktioner, klasser och metoder omringas av en blankrad.

Blankrader kan användas i funktioner för att dela upp koden i logiska sektioner.

Kommentarer

"You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now."

- Linus Torvalds

Om en sats ej är helt självförklarande bör kommentarer användas för att beskriva syftet med, och tanken bakom, satsen. Kommentarer ska alltid vara fullständiga meningar och stämma överens med koden som beskrivs. Kommentarer ska ej användas till att beskriva speciell syntax eller specifika operationer. Kommentarerna kompletterar koden så att de tillsammans gör programmet lätt att förstå.

x = 2 # Set x to 2

Onödig kommentar. Förklara istället varför x tilldelas 2.

x = 2 # Start with the lowest prime number

Typer av kommentarer

Kommentarer är markerade sektioner av koden som ignoreras av interpretatorn. Likt många andra programmeringsspråk har Python (på sätt och vis) två sorters kommentarer.

# Detta är en enradskommentar!

"""
Detta är en sträng som kan fungera som blockkommentar!
Den kan täcka flera rader!
"""

Python har egentligen inte blockkommentarer -- men den har något annat som många språk saknar, nämligen strängar som sträcker sig över fler än en rad. Om man skriver en sådan sträng som om den vore en sats, betyder det att Python ska evaluera strängen och sedan inte göra något med värdet. Det fungerar då precis som om det vore en kommentar. På precis samma sätt kan man egentligen vilka uttryck som helst inuti en funktion:

def test():
    values = [1000, 2500, 400]
    42
    print(values)

>>> test()
[1000, 2500, 400]

Mittenraden ovan ber Python att räkna ut 42. Den räknar ut att detta är 42. Sedan fortsätter den med nästa rad.

Docstrings

"A universal convention supplies all of maintainability, clarity, consistency, and a foundation for good programming habits too. What it doesn't do is insist that you follow it against your will. That's Python!"

-Tim Peters on comp.lang.python, 2001-06-16

Python dokumenterar funktioner (med mera) med hjälp av så kallade docstrings, vilket är strängar placerade direkt efter def-raden för en funktion, metod, modul eller klass. Detta kan se ut så här:

def divides(k, n):
    """ Checks if k divides n """
    return n % k == 0

Som synes ovan säger standarden att man ska skriva dessa strängar med som blocksträngar, med tredubbla citattecken -- men de är faktiskt bara vanliga strängar. Det råkar bara vara så att när det första i en funktion är en sträng, antar Python att den är tänkt som dokumentation.

Docstrings får några speciella egenskaper som vi inte kommer gå in på, men ska kortfattat förklara vad funktionen gör. I kursen kräver vi väl skrivna docstrings på alla funktioner från och med labb 5 och på tentamen.

Docstrings ska som andra kommentarer vara skrivna i flytande text. För funktioner ska en docstring summera funktionens beteende, dokumentera dess argument, returvärde och sidoeffekter. Finns många argument kan dessa beskrivas med en rad per argument.

Docstring utgör en funktions kontrakt. En funktion som utför det och endast det som är specifierat i dess docstring kan anses vara korrekt.

Det finns ytterligare krav enligt standarden som inte behöver uppfyllas i denna kurs. För fullständig specifikation, se PEP-257.

def sum(n):
    """ 
    Returns the sum of all integers from 0 to n.
    """
    res = 0
    for i in range(n + 1):
        res += i
    return res

Radlängd

För att skriva lättlästa program brukar man begränsa längden som en rad får ha. PEP-8 specificerar att Python-kod maximalt ska vara 79 tecken lång för att behålla läsbarheten. Vidare ska kommentarer och docstrings enbart vara 72 tecken långa enligt specifikationen.

Att det just är 79 tecken som är bredden för koden har troligen sitt ursprung i att terminalfönster som standard har en bredd på 80 tecken (från den tid när hårdvaruterminaler hade exakt 80 tecken per rad) samt att markören använder ett tecken. Det finns dock fler anledningar till att ha en radbredd som enbart är 79 tecken i dagsläget. PEP-8 nämner som exempel att det gör det möjligt att ha flera filer öppna vid sidan av varandra.

För att få emacs att hålla reda på radlängden åt en kan man lägga till följande emacs-lisp kod i filen ~/.emacs (och skapa den om den inte finns):

(require 'whitespace)
(setq whitespace-style '(face empty tabs lines-tail trailing))
(setq whitespace-line-column 79)
(global-whitespace-mode t)

Det kommer att lägga till en annan bakgrundsfärg på alla tecken efter det 79:e tecknet.

Mellanrum och onödiga tecken

Eftersom man strävar efter att skriva läsbar kod är det även värt att nämna något om mellanrum i koden och onödiga tecken. Det man kan tänka på med mellanrum i koden är att det inte finns någon regel som inbegriper alla fall. Som riktlinje kan man dock ha att man ska efterlika helt vanlig text. Till exempel, ha ett mellanslag efter ',' och separera gärna långa aritmetiska uttryck på lämpliga ställen. Jämför:

x=fn(3*4+5/3*26-3,3+4%9-3*5)

Med:

x = fn(3*4 + 5/3*26 - 3, 3 + 4%9 - 3*5)

Vill man kan det hela delas upp på två rader enligt följande:

x = fn(3*4 + 5/3*26 - 3,
       3 + 4%9 - 3*5)

Notera här att det inte finns med ett '' i slutet av första raden. Det är för att man är "inuti" en öppen parentes (i ett funktionsanrop). Satsen kan omöjligt ha tagit slut efter första raden eftersom man behöver hitta en högerparentes först, så Python antar att även nästa rad är med i satsen utan att man behöver ett ''.

Enkelhet

Koden är inte en plats att briljera med sina kunskaper i ett språks konstigaste funktionaliteter, enkel och tydlig kod är att föredra.

print("Uneven" if x%2 else "Even")

if x % 2 == 0:
    print("Even")
else:
    print("Uneven")

Även om dessa två lösningar gör samma sak kan det nedre alternativet vara mer lättläst. Detta för att det använder en if-sats och inte använder att 0 tolkas som False i Python.

Testbarhet

"If it's hard to document, it's probably wrong. If it's hard to test, it's almost certainly wrong."

- Okänd upphovsman

Testbarhet är måttet på hur enkelt något är att testa (ej i vilken grad det faktiskt testas), det kan vara ett system, ett program eller en individuell funktion. Moduläritet är en viktig del i hur testbart ett program är. Det är ett mått på i vilken grad något är uppdelat i självständiga delar. Detta kan vara funktioner, klasser, moduler eller liknande. Det här leder till att det blir enkelt att testa varje del för sig.

Moduläritet innebär också att varje del har ett specifikt ansvar (eng. separation of concerns). Med specifikt ansvar menar vi att man endast utför en uppgift och inget annat. Kommunikation mellan delar bör ske strikt och kontrollerat.

Länkar

PEP-8 PEP-257

För en kort dikt om hur pythonkod bör skrivas, skriv följande kommando i en Python-interpretator:

import this

Tillhörande quiz

Finnes här


Sidansvarig: Peter Dalenius
Senast uppdaterad: 2021-12-03