1. Styleguide
I programmering 1 kursen har vi lärt oss grunderna i programmering.
Den kod ni skriver gör för de mesta det den ska, men en erfaren programmerare kan ofta se på koden om det är en nybörjare,
mer erfaren eller ett proffs som skrivit koden.
Vad behöver ni göra för att få er kod att se mer proffsig ut? Hur ska ni ta nästa steg i att få er kod att se mer proffsig
ut?
Genom hela kursen har läsbarheten varit i fokus, dvs att koden är enkel att läsa och förstå. De exempel som tagits upp är bl.a. att ha beskrivande namn på variabler och funktioner, kommentarer, indentering och att dela upp programmet i funktioner som har en uppgift.
På Pythons officiella sida finns Python Developer's Guide
finns olika PEPs (Python Enhancement Proposals), fritt översatt Pythons förbättringsförslag.
Vi ska titta närmare på delar i PEP 8 (Style Guide for Python Code) och
PEP 20 (The Zen of Python).
De innehåller en stilguide för Python kod och hur man ska tänka för att få bra design.
De flesta regler och tankesätt är faktiskt också applicerbara på alla programmeringsspråk.
1.1 Indentering
Var konsekvent och indentera alltid med lika många mellanslag i hela koden. 4 mellanslag är det som rekommenderas i Python.
# Correct (according to Style Guide for Python Code)
if max_count > 4:
print(f'Max antal är {max_count}')
# Wrong (according to Style Guide for Python Code)
if max_count > 4:
print(f'Max antal är {max_count}')
Källa: PEP 8 - indentation
1.2 Beskrivande namn och snake case
Använd beskrivande namn för variabler och funktioner för det gör koden enklare att läsa och förstå.
Var konsekvent med bara svenska namn eller helst bara engelska namn.
För engelska gör att fler kan förstå och t.ex. lättare få hjälp på olika forum. Programmeringsspråken i sig är ju oftast
också på engelska. Försöker man skriva svenska namn blir det ofta en del på svengelska också.
Det finns flera olika stilar för att namnge t.ex. variabler. De flesta använder snake case i Python.
Det innebär att variabler bör namnges med små bokstäver, med ord åtskilda av understreck.
# Correct
max_count = 1
min_temperature = 20.4
Även funktionsnamn bör namnges enligt snake case.
# Correct
def count_average():
pass
def add_elements(lst):
pass
Nyckelordet pass gör inget, men man slipper få syntaxfel i koden, eftersom t.ex. tomma funktioner ger syntaxfel.
Källor:
PEP 8 - descriptive naming styles
PEP 8 - function and variable names
1.3 Kommatecken, mellanslag och parenteser
Ett och endast ett mellanslag vid tilldelning både före och efter tilldelningstecknet.
# Correct
max_count = 10
# Wrong
max_count=10
max_count =10
max_count= 10
# Correct
x = 1
y = 2
long_variable = 3
# Wrong
x = 1
y = 2
long_variable = 3
Ett och endast ett mellanslag vid t.ex. utökade tilldelningsoperatorer och andra operatorer.
# Correct
count += 1
count = count + 1
# Wrong
count+=1
count +=1
count=count+1
count=count + 1
count = count+1
# Correct
if x == 10:
pass
# Wrong
if x==10:
pass
if x ==10:
pass
if x== 10:
pass
Undvik mellanslag direkt före kolon.
# Correct
if max_count == 10:
pass
# Wrong
if max_count == 10 :
pass
Undvik extra mellanslag vid funktions- och metod-anrop.
# Correct
print(max_count)
# Wrong
print (max_count)
print( max_count )
print( max_count)
print(max_count )
Ha ett mellanslag efter kommatecknet i argumentlistan vid funktions- och metod-anrop.
# Correct
calculate(a, b, c)
# Wrong
calculate(a,b,c)
calculate( a,b,c )
calculate(a,b, c)
Ha ett mellanslag efter kommatecknet i parameterlistan vid funktionsdeklarationer.
# Correct
def calculate(a, b, c):
pass
# Wrong
def calculate(a,b,c):
pass
def calculate( a,b,c ):
pass
def calculate(a,b, c):
pass
Samma regler för for-satsen.
# Correct
for i in range(2, 7):
pass
# Wrong
for i in range (2, 7):
pass
for i in range(2,7):
pass
Undvik mellanslag direkt inom parenteser (), hakparenteser [] och krullparenteser {}.
# Correct
lst[1] = 2
lst[i] = 4
# Wrong
lst [1] = 2
lst[ 1 ] = 2
lst [i] = 4
lst[ i ] = 4
Inga parenteser i if- och while-satser.
# Correct
if x == 10:
pass
while x < 10 and y <= 10:
pass
# Wrong
if (x == 10):
pass
if(x == 10):
pass
while(x < 10 and y <= 10):
pass
while (x < 10 and y <= 10):
pass
Den här rekommendationen kan kännas lite godtycklig och "lurig", men använd ditt eget omdöme och var konsekvent. Om operatorer med olika prioritet används kan du överväga att lägga till ett mellanslag runt operatorerna med lägst prioritet. Använd ditt eget omdöme, använd dock aldrig mer än ett mellanslag och ha alltid samma antal mellanslag på båda sidor om en operator.
# Correct
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
# Wrong:
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
Källor:
PEP 8 - whitespace in expressions and statements
PEP 8 - other recommendations
1.4 Tomma rader
Om inte funktioner definieras i en egen modul bör de alltid definieras högst upp i koden (efter eventuella import-satser).
Omge funktionsdefinitioner med 2 tomma rader.
# Correct
import math
def my_first_function():
pass
def my_second_function():
pass
def my_third_function():
pass
# Wrong
import math
def my_first_function():
pass
def my_second_function():
pass
def my_third_function():
pass
Använd tomma rader sparsamt för att ange logiska avsnitt.
# Correct and more readable
import matplotlib.pyplot as plt
# Function that calculates 2x + 3
def f(x):
return 2*x + 3
# Draw grid, labels on axis and title
plt.xlabel('x')
plt.ylabel('y')
plt.title('Diagram')
plt.grid()
# Calculate y values and append to list
x_list = [-2, -1, 0, 1, 2]
y_list = []
for x in x_list:
y = f(x)
y_list.append(y)
# Plot the diagram
plt.plot(xLista, yLista)
plt.savefig('plot.png')
# Wrong and less readable
import matplotlib.pyplot as plt
# Function that calculates 2x + 3
def f(x):
return 2*x + 3
# Draw grid, labels on axis and title
plt.xlabel('x')
plt.ylabel('y')
plt.title('Diagram')
plt.grid()
# Calculate y values and append to list
x_list = [-2, -1, 0, 1, 2]
y_list = []
for x in x_list:
y = f(x)
y_list.append(y)
# Plot the diagram
plt.plot(xLista, yLista)
plt.savefig('plot.png')
Källa: PEP 8 - blank lines
1.5 Funktioner
Funktioner med mycket kod är svåra att läsa, förstå och underhålla (rätta fel i och vidareutveckla).
De bör vara ungefär 5 - 15 rader långa.
Undvik print-satser i funktioner, returnera värden istället och låt huvudprogrammet skriva ut om så önskas,
vilket ger bättre återanvändbarhet och flexibilitet.
Källa: 5 Ways to Improve Your Code
1.6 Tecken för att markera strängar
Två olika sätt kan användas för att markera ett värde som sträng, enkla eller dubbla citattecken. Välj ett av sätten och var konsekvent.
# 2 ways to quote strings
str_1 = 'hello'
str_2 = "howdy"
Källa: PEP 8 - string quotes
1.7 F-strängar
Använd alltid f-strängar vid formatering/utskrift av värden. Andra äldre sätt är långsammare och ger sämre läsbarhet. Det finns alltså 3 olika sätt att formatera strängar, men f-strängar är snabbast och ger den bästa läsbarheten.
# Correct, f-strings introduced in Python 3.6
name = "Eric"
age = 74
print(f"Hello, {name}. You are {age}.")
# Wrong
name = "Eric"
age = 74
# Old school original formatting
print("Hello, %s. You are %s." % (name, age))
# Old, introduced in Python 2.6
print("Hello, {}. You are {}.".format(name, age))
Källa: Real Python: F-strings
1.8 Flera satser på samma rad
Flera satser på samma rad avråds i allmänhet.
# Correct:
if foo == 'blah':
do_blah_thing()
do_one()
do_two()
do_three()
do_one()
do_two()
do_three()
# Wrong, rather not
if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()
do_one(), do_two(), do_three()
# Correct
for x in lst:
total += x
while t < 10:
t = delay()
# Wrong, rather not
for x in lst: total += x
while t < 10: t = delay()
# Correct
if foo == 'blah':
do_blah_thing()
else:
do_non_blah_thing()
if foo == 'blah':
one()
two()
three()
# Wrong, definitely not
if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()
if foo == 'blah': one(); two(); three()
Källa: PEP 8 - several statements in line
1.9 Globala variabler
Använd aldrig globala variabler i funktioner. Det gör att svårhittade buggar kan uppstå, återanvändbarheten försämras och koden får sämre läsbarhet.
# Example where variables x and y are global variables and not used in the function
def my_function(a, b):
return a + b + 3
x = 4
y = 5
ret = my_function(x, y)
print(ret)
# Bad design to access global variables in the function
def my_function(a, b):
return a + b + 3 + x
x = 4
y = 5
ret = my_function(x, y)
print(ret)
# To prevent this bad design a main function can be introduced.
# Now variables x and y are local in the function main and can't be accessed in my_function().
def my_function(a, b):
return a + b + 3
def main():
x = 4
y = 5
ret = my_function(x, y)
print(ret)
main()
Källa: PEP 8 - global variable names
1.10 Kommentarer
Kommentarer som strider mot koden är värre än inga kommentarer.
Prioritera alltid att hålla kommentarerna uppdaterade när koden ändras!
Kommentarer ska vara fullständiga meningar. Det första ordet ska börja med stor bokstav, såvida det inte är en identifierare (variabel/funktions)som börjar med liten bokstav (ändra aldrig fallet med identifierare!)
Skriv dina kommentarer på engelska, såvida du inte är 100% säker på att koden aldrig kommer att läsas av personer som inte talar ditt språk.
Block-kommentarer
Block-kommentarer gäller vanligtvis för någon (eller alla) satser som följer dem och är indragna till samma nivå som den koden.
Varje rad i en blockkommentar börjar med ett # och ett enda mellanslag. Kommentaren skrivs alltid före koden den avser.
# Correct
# Calculate from Fahrenheit to Celsius
temperature_celsius = 5/9 * (temperature_fahrenheit-32)
# Wrong
temperature_celsius = 5/9 * (temperature_fahrenheit-32)
# Calculate from Fahrenheit to Celsius
# Correct
# Check what season the temperature indicates
if temperature_celsius > 20:
# Summer
print(f'It is like a warm summerday, the temperature is {temperature_celsius}')
elif temperature_celsius < 0:
# Winter
print(f'It is like a cold winterday, the temperature is {temperature_celsius}')
# Wrong
# Check what season the temperature indicates
if temperature_celsius > 20:
# Summer
print(f'It is like a warm summerday, the temperature is {temperature_celsius}')
elif temperature_celsius < 0:
# Winter
print(f'It is like a cold winterday, the temperature is {temperature_celsius}')
# Wrong
# Check what season the temperature indicates
# Summer
if temperature_celsius > 20:
print(f'It is like a warm summerday, the temperature is {temperature_celsius}')
# Winter
elif temperature_celsius < 0:
print(f'It is like a cold winterday, the temperature is {temperature_celsius}')
Inline-kommentarer
En inline-kommentar är en kommentar på samma rad som koden. Kommentaren ska ha minst 2 mellanslag före.
Använd inline-kommentarer sparsamt.
Inline-kommentarer är onödiga och i själva verket distraherande om de anger det uppenbara, s.k. papegoj-kommentarer.
# Correct
x = x + 1 # Compensate for border
# Wrong, don't do this
x = x + 1 # Increment x
a = a - 3 # Subtract 3 from a
Tala inte om vad som görs i inline-kommentarerna utan varför. Då kan det vara användbart.
Källor:
PEP 8 - comments
PEP 8 - block comments
PEP 8 - inline comments
1.11 Dokumentations-strängar
En dokumentations-sträng (docstring) är en sträng som förekommer som det första satsen i en definition av modul och funktion.
Skriv docstrings för alla publika moduler och funktioner.
# Example of one liner docstring.
def fahrenheit_to_celsius(temperature_fahrenheit):
"""Convert temperature from fahrenheit to celsius."""
pass
# Example of multiline docstring, the """ that ends a multiline docstring should be on a line by itself
def celsius_to_fahrenheit(temperature_celsius):
"""Convert temperature from celsius to fahrenheit.
Return the temperature in fahrenheit.
"""
pass
Bl.a. utvecklingsmiljöernas autocomplete-funktionalitet visar dessa texter för att ge oss information när vi skriver kod.
Det går också att automatiskt extrahera online-hjälp från koden.
Källor:
PEP 8 - documentation strings
PEP 257 - docstring conventions
1.12 Sammanfattning
- Var konsekvent med allt du väljer, vilket betyder att använd alltid samma sätt överallt i hela koden.
- 4 mellanslag vid indentering.
- Beskrivande namn och snake case.
- Mellanslag mellan operatorer.
- Undvik mellanslag direkt före kolon.
- Undvik extra mellanslag vid funktions- och metod-anrop.
- Undvik mellanslag direkt inom parenteser (), hakparenteser [].
- Inga onödiga parenteser i if- och while-satser.
- Omge funktions-definitioner med 2 tomma rader.
- Använd tomma rader sparsamt för att ange logiska avsnitt i koden.
- Välj ett av sätten för att markera strängar (enkla eller dubbla citattecken) och var konsekvent.
- Använd alltid f-strängar vid formatering/utskrift av värden.
- Flera satser på samma rad avråds i allmänhet.
- Använd aldrig globala variabler i funktioner.
- Kommentera koden.
- Skriv docstrings för alla publika moduler och funktioner.
2. Design-regler
"The Zen of Python" av Tim Peters är 20 riktlinjer för designen av Python-språket. Din Python-kod behöver inte nödvändigtvis följa dessa riktlinjer, men de är bra att komma ihåg. Många av dem fungerar dessutom för alla programmeringsspråk.
Zen of Python är ett påskägg (dolt skämt) som visas om du kör koden:
import this
ger utskriften
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Dessa riktlinjer är i slutändan åsikter som i vissa situationer kan argumenteras för eller emot. Liksom alla bra uppsättningar av riktlinjer motsäger de sig
ibland. Vi ska titta närmare på några av dem.
Beautiful is better than ugly.
Programmerare skriver ofta kod snabbt utan att bekymra sig för läsbarheten. För projekt i arbetslivet brukar man säga att för ett programs livstid består nyutveckling för 10-20% av tiden och övrig tid är underhålla av koden. Det är alltså viktigt att koden är läsbar för alla. Det är ofta andra programmerare som underhåller koden än de som ursprungligen skrev koden.
Explicit is better than implicit.
Det bättre att koden är detaljerad och tydlig än att olika avancerade specialfunktioner används.
Simple is better than complex. Complex is better than complicated.
Använd enkla lösningar för att lösa enkla problem. De komplexa lösningarna ska användas för komplexa problem. Föredra enkelhet före komplexitet, men känn till begränsningarna för den enkla lösningen.
Sparse is better than dense.
Programmerare gillar ofta att klämma in så mycket funktionalitet som möjligt i så lite kod som möjligt, till exempel som följande kodrad:
print('\n'.join("%i bytes = %i bits which has %i possible values." % (j, j*8, 256**j-1) for j in (1 << i for i in range(8))))
Även om kod som denna kan imponera på dina vänner, kommer den att göra dina medarbetare rasande som måste försöka förstå den. Kod som sprids över flera rader är ofta lättare att läsa än få täta kodrader.
Readability counts.
Att strcmp() uppenbarligen betyder "string compare" för någon som har programmerat i C sedan 1970-talet, har moderna datorer tillräckligt med minne för att skriva ut hela funktionsnamnet.
Släpp inte vokaler från dina namn eller skriv alltför kort kod.
Kod läses oftare än den är skrivs, så tydlig, läsbar kod med beskrivande namn på identifierare (variabler/funktioner) är viktigare än kortare, odokumenterad kod.
Special cases aren’t special enough to break the rules. Although practicality beats purity.
Dessa två riktlinjer motsäger varandra. Programmering är full av riktlinjer och "best practices" som programmerare bör sträva efter i sin kod. Att strunta i riktlinjer för att snabbt koda en lösning kan vara frestande, men kan leda till inkonsekvent och svårläst kod. Att strikt följa regler kan också ibland resultera i mycket abstrakt, oläslig kod. Att kunna balansera mellan dessa riktlinjer blir lättare med erfarenhet. Och med tiden lär du dig inte bara reglerna utan också när du ska bryta mot dem.
Errors should never pass silently. Unless explicitly silenced.
Felhantering är inget som tagits ännu upp på kursen.
Bara för att programmerare ofta ignorerar felmeddelanden betyder inte att programmet ska sluta skicka dem. Tysta fel kan inträffa när funktioner returnerar felkoder eller None istället för att skapa undantag (exceptions).
Dessa två riktlinjer säger att det är bättre för ett program att misslyckas snabbt och krascha än att tysta felet och fortsätta att köra programmet. De buggar som oundvikligen händer senare blir svårare att felsöka eftersom de är långt borta från den ursprungliga orsaken. Även om du alltid kan välja att uttryckligen ignorera de fel som dina program orsakar, var bara säker på att du göra medvetna val.
In the face of ambiguity, refuse the temptation to guess.
Om din kod inte fungerar finns det en anledning och endast noggrant, kritiskt tänkande kan lösa det. Undvik frestelsen att bara prova lösningar tills något verkar fungera utan att du egentligen förstår varför för ofta har du bara maskerat problemet snarare än löst det. Och det kommer att dyka andra fall då koden inte fungerar.
Använd heller aldrig kod som du "hittat" på internet om du inte fullt ut förstår den.
If the implementation is hard to explain, it’s a bad idea. If the implementation is easy to explain, it may be a good idea.
Om du märker att din kod blir väldigt komplicerad finns det oftast en enklare lösning för att lösa problemet. Fundera en extra gång om
det finns bättre sätt att lösa problemet, oftast finns det det.
Program måste kunna förstås inte bara av programmeraren som skrev den, utan också av andra programmerare som underhåller koden.
Dessa två riktlinjer påminner oss om att om "högpresterande" kod är så komplicerad att den är omöjligt för programmerare att förstå och
felsöka, då är det dålig kod. Men tyvärr, bara för att det är lätt att förklara ett programs kod för någon annan betyder det inte att
det är bra kod. Programmering kan vara svårt ibland.
Källor:
PEP 8 (Style Guide for Python Code)
PEP 20 (The Zen of Python)
pep8.org
The zen of Python explained
Centralt innehåll
Fakta
Begrepp
Läsbarhet: Att koden är enkel att läsa och förstå.
PEP: Python Enhancement Proposals.
PEP 8: Style Guide for Python Code.
PEP 20: The Zen of Python.
whitespace: Blankt tecken, ett tecken som inte syns, kan t.ex. vara mellanslag, tab etc.
F-sträng: Formatering av strängar.
Block-kommentar: Gäller för någon (eller alla) satser som följer och är indragna till samma nivå som den koden.
Inline-kommentar: En kommentar på samma rad som koden.
Papegoj-kommentar: Anger det uppenbara eller upprepning av koden, dvs tillför inget utan är bara distaraherande och minskar läsbarheten.
Dokumentations-sträng: Beskrivning av funktionaliteten som första sats i en definition av modul och funktion.
Länkar
PEP 8 - Style Guide for Python Code
PEP 8 - descriptive naming styles
PEP 8 - function and variable names
PEP 8 - whitespace in expressions and statements
PEP 8 - other whitespace recommendations
PEP 8 - several statements in line
Övningar
Inlämningsuppgift
PEP 8
Se till att din kod ser proffsig ut. Följ reglerna i PEP 8 på din kod från bank-uppgiften.
4 mellanslag som indentering (1.1)
Beskrivande engelska namn och snake case (1.2)
Korrekta mellanslag vid tilldelning, argument, parametrar och uttryck (1.3)
Inga parenteser i if- och while-satser (1.3)
2 tomma rader före och efter funktionsdefinitionerna (1.4)
Inga print-satser i funktionerna redraw och deposit (1.5)
Använd f-strängar vid utskrifter (1.7)
Använd en main-funktion för att undvika användning av globala variabler (1.9)
Korrekt placering, rätta antal mellanslag och stor första bokstav i kommentarer (1.10)