3.1. Lommeregner (konsolbaseret)¶
I denne introduktion til programmering og Python udvikles en interaktiv konsolbaseret (tekstbaseret) lommeregner som vist på figur 3.1.
Bemærk
Introduktionerne antager du har gennemgået Installation og Første Python-program og første fejl.
Programmet du om lidt bliver hjulpet med at udvikle skal give brugeren mulighed for at vælge i mellem forskellige regneoperationer ved at taste et tal og trykke [Enter], f.eks. 1 for at udregne a+b. Herefter skal brugeren kunne indtaste de tal eller størrelser som udregningen kræver. Til sidst skal programmet vise brugeren resultatet af regneoperationen og give brugeren muligheden for at vælge en ny regneoperation.
3.1.1. Brugerinput og kommentarer¶
Opret et nyt program Python-program med et fornuftigt navn,
f.eks. calculator.py
,
og gem det et fornuftigt sted,
f.eks. i en mappe kaldet programmering/introduktion/
.
Giv programmet følgende indhold:
Anbefaling
Skriv koden manuelt (tegn for tegn) ind i din teksteditor i stedet for at kopiere den og indsætte den (copy-and-paste).
Hvis du skriver Kildekode 3.1 ind i din teksteditor så har du skrevet 5 linjers Python-kode. Hvis du kopiere og indsætter det har du skrevet 0 linjers Python-kode.
1 2 3 4 5 6 | # A simple calculator made as an introduction to programming
a = input("Write a number: ")
b = input("Write another number: ")
c = a+b
print(c)
|
Kør programmet og når programmet beder dig indtaste et tal skriver du blot et eller flere tal på tastaturet og trykker [Enter]. I din konsol bør du se noget som minder om nedenstående:
1 2 3 | Write a number: 13
Write another number: 37
1337
|
Meget muligt forstår du ikke hvorfor eksemplet
skriver 1337
som output (Kildekode 3.2, linje 3).
Linje 6 (Kildekode 3.1) havde måske ledt dig til at forvente output 50
.
Den opførsel vender vi tilbage til.
Linje 1 (Kildekode 3.1) som starter med tegnet #
1 er en kommentar.
Python ignorerer linjen,
men kommentarer er vigtige fordi de hjælper mennesker,
f.eks. dig selv i fremtiden,
med at forstå koden og programmets formål.
Anbefaling
Start alle programmet med en eller flere linjers kommentarer som beskriver hvad programmet gør og hvorfor du har skrevet det.
I linje 3 bruges funktionen input()
til at bede brugeren om et tal (input).
"Write a number: "
kaldes argumentet til funktionen input()
,
og vises på skærmen hvorefter programmet venter på brugeren skriver noget.
Programmet fortsætter når brugeren trykker [Enter].
Hvad end brugeren skriver gemmes i en variabel a
således brugerens input kan bruges igen senere i programmet,
f.eks. i linje 5.
I linje 5 produceres en ny værdi baseret på brugerens input
(gemt i variablene a
og b
).
Den nye værdi produceres af operatoren +
og gemmes i en ny variabel c
.
Variablene a
og b
i linje 5 kaldes også operander til operatoren +
.
Endlig skrives eller printes resultatet på skærmen i linje 6
vha. funktionen print()
som kaldes med variablen c
som argument.
3.1.2. 13+37=1337? (datatyper)¶
Hvorfor giver programmet så output 1337
hvis brugeren skriver tallene 13 og 37 som input?
Det er fordi værdierne gemt i variabel a
og b
ikke er tal, men tekst
og hvis operatoren +
får tekst som operander
så sætter den teksten sammen.
Hvis operatoren havde fået heltallene 13 og 37 som operander havde den,
som forventet,
summeret tallene og produceret heltallet 50.
Hvordan konvertere man så teksten 13 og 37 til heltallene 13 og 37
således +
kan producere heltallet 50?
Det gøres med funktionen int()
som konvertere sit argument
fra tekst til et heltal
(int er en forkortelse for integer, engelsk for heltal).
Opret et nyt Python-program med et navn såsom test_data_types.py
og giv det følgende indhold og kør programmet.
1 2 3 4 5 6 7 8 9 10 11 12 | # Test of data types (+ operator with text and numbers)
c = "13"
d = input("Type 37 and press [Enter]: ")
e = 37
cc = int(c)
dd = int(d)
print(c+d)
print(cc+dd)
print(cc+e)
print(c+e)
|
1 2 3 4 5 6 7 8 | Type 37 and press [Enter]: 37
1337
50
50
Traceback (most recent call last):
File "test_data_types.py", line 11, in <module>
print(c+e)
TypeError: can only concatenate str (not "int") to str
|
Outputtets linje 2 (Kildekode 3.4)
er resultatet af programmets linje 9 (Kildekode 3.3)
hvor +
operatoren får "13"
og "37"
som operander
og resultatet bliver teksten 1337.
I programmets linje 6 og 7 bruges funktionen int()
til at konvertere teksten indeholdet i variablene c
og d
til heltal (integers) og resultatet gemmes i variablene cc
og dd
.
Outputtets linje 3 er resultatet af programmets linje 10
hvor +
får operanderne cc
og dd
(som nu er heltal)
og resultatet bliver nu tallet 50 (selvfølgelig printet som teksten 50 på skærmen).
Outputtets linje 4 viser også 50, resultatet af cc+e
(programlinje 11).
Variablen e
er direkte tildelt heltalsværdien 13
(i modsætning til tekstværdien "13"
).
I Python og mange andre programmeringssprog repræsentere "13"
teksten 13
og kaldes en string (engelsk) eller tekststreng (dansk)
hvorimod 13
repræsentere heltallet 13 (engelsk: integer).
Outputtets linje 5-8 viser en fejl TypeError: can only concatenate str (not "int") to str
.
Som fejlmeddelelsen angiver sker fejlen i programmets linje 12
hvor variablene c
og e
gives som operander til +
.
Det virker ikke fordi de to variable indeholder værdier af forskellige typer
(str: tekst/string, int: heltal/integer).
Mens man måske kan retfærdiggøre resultatet af teksten "2"+2
skulle være "4"
eller 4
,
er det svært at retfærdiggøre "horse" + 7
ikke skal give en fejl.
Lad os bruge funktionen type()
til at bekræfte datatypen af de variable
vi lige har omtalt. Modificer programmet således:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # Test of data types (+ operator with text and numbers)
c = "13"
d = input("Type 37 and press [Enter]: ")
e = 37
cc = int(c)
dd = int(d)
print(c+d)
print(cc+dd)
print(cc+e)
#print(c+e) # TypeError from int + str
print(type(c))
print(type(d))
print(type(e))
print(type(cc))
|
1 2 3 4 5 6 7 8 | Type 37 and press [Enter]: 37
1337
50
50
<class 'str'>
<class 'str'>
<class 'int'>
<class 'int'>
|
Linje 12 er nu det man kalder udkommenteret,
#
som første tegn på linjen gør linjen ignoreres.
Således resultere programmet ikke længere i en fejl.
Bemærk der også er tilføjet en ekstra kommentar
i slutningen af linjen som forklarer hvad
der er specielt ved denne linje.
Linjerne 14-17 af benytter funktionen type()
til at bestemme, eller undersøge, datatypen af programmets variable.
Fra outputtets linje 5, som stammer fra programmets linje 14,
kan vi se at c
er en string (str er en forkortelse for string),
og dermed er d
også en string.
e
og cc
er integers eller heltal (int er forkortelse for integer).
Udfordring
Gør outputtet fra testprogrammet mere læsevenligt
ved at give print()
-funktionen flere argumenter som vist her:
1 | print("some descriptive text", some_variable)
|
Udfordring
Kommatal skrives i Python og mange andre programmeringssprog som 2.5
med decimal punktum,
hvilket vidner om engelsk talende landes rolle i udviklingen af computeren.
Kommatals datatype kaldes float og strings kan konverteres til floats vha. funktionen float()
.
Lav samme test som vi lige har lavet med strings og integers, med floats i stedet.
3.1.3. Næste version af lommeregnerprogrammet¶
Med vores nyfundne viden om datatyperne integers og strings (muligvis floats, se udfordringen ovenfor)
kan vi nu sørge for at lommeregnerprogrammet
bruger brugerens input som tal og ikke som tekst,
altså få programmet til at producere outputtet 50
når brugeren giver 13 og 37 som input.
Udfordring
Benyt det du lige har lært om datatyper til at
sørge for lommeregnerprogrammet benytter brugerens input
som tal og producere 50
som output når brugeren
giver 13 og 37 som input.
En mulig ny version af programmet (og løsning på ovenstående udfordring) kunne se ud på følgende måde, men der er mange andre løsninger som vil give præcist samme output:
1 2 3 4 5 6 7 8 | # A simple calculator made as an introduction to programming
in1 = input("Write a number: ")
in2 = input("Write another number: ")
a = int(in1)
b = int(in2)
c = a+b
print(c)
|
1 2 3 | Write a number: 13
Write another number: 37
50
|
I løsningen her er der oprettet to ny variable in1
og in2
(in for input)
til at gemme de rå brugerinput (strings)
og de to variable a
og b
bruges nu til at gemme de konverterede værdier (integers),
men man kunne også have skrevet a = int(input("Write a number: "))
og undladt variablene in1
og in2
.
Lad os forbedre brugervenligheden af programemt ved at sørge for programmet fortæller brugeren hvad de to tal skal bruges til og læsevenligheden af outputtet ved at tydeliggøre hvilken linje der er resultatet:
1 2 3 4 5 6 7 8 9 | # A simple calculator made as an introduction to programming
print("Calculate a + b:")
in1 = input("a=")
in2 = input("b=")
a = int(in1)
b = int(in2)
c = a+b
output = "{}+{}={}".format(a,b,c)
print(output)
|
1 2 3 4 | Calculate a + b:
a=13
b=37
13+37=50
|
Det pæne output, f.eks. 13+37=50
dannes i programmets linje 8
vha. metoden str.format()
som erstattet {}
i en string med noget andet.
Første sæt {}
i "{}+{}={}"
erstattes med variablen a
,
næste sæt med b
og sidste sæt med c
.
Resultatet af er en ny string som gemmes i variablen output
.
3.1.4. Tillad gentagne udregninger¶
Lommeregnerprogrammet, som demonstreret i starten, giver brugeren mulighed for at lave mere end én udregning uden at skulle genstarte programmet. Vi har altså behov for at kunne gentage linje 2-9 (Kildekode 3.9) flere gange (evt. uendeligt mange gange). Det kan gøres med et såkaldt while-loop (eller while-løkke):
1 2 3 4 5 6 7 8 9 10 11 | # A simple calculator made as an introduction to programming
while True:
print("Calculate a + b:")
in1 = input("a=")
in2 = input("b=")
a = int(in1)
b = int(in2)
c = a+b
output = "{}+{}={}".format(a,b,c)
print(output)
|
Linje 3 while True:
gentager de efterfølgende linjer 4-11
et uendeligt antal gange.
Linjerne 4-11 bliver gentaget fordi koden på disse linjer
er indrykket (engelsk: indented) i forhold til linje 3.
Prøv
Prøv at rykke forskellige linjers kode udenfor while-loopet.
Enten ved at placere dem over linjen med while True:
eller ved ikke at rykke dem ind.
Hvordan opfører programmet sig nu og hvorfor?
3.1.5. Tillad brugeren at stoppe programmet¶
Som programmet er nu kan programmet ikke stoppes af brugeren, det bliver ved med at tilbyde udregning af a+b i det uendelige.
Et loop, f.eks. vores while-loop, kan stoppes vha.
Python-udsagnet (engelsk: statement) break
.
Hvis Python udfører en linje hvorpå der står break
så stopper det loop Python er i færd med at udføre.
Lad os prøve at indsætte break
et sted i vores loop:
1 2 3 4 5 6 7 8 9 10 11 12 | # A simple calculator made as an introduction to programming
while True:
print("Calculate a + b:")
in1 = input("a=")
in2 = input("b=")
break
a = int(in1)
b = int(in2)
c = a+b
output = "{}+{}={}".format(a,b,c)
print(output)
|
Nu stopper programmet selvfølgelig midt i en interaktion med brugeren,
det stopper efter at have spurgt om de to inputs.
Uanset hvor vi placere break
(prøv evt. andre placeringer)
så vil programmet enten stoppe et uhensigtsmæssigt sted
eller stoppe efter en enkelt udregning.
Der er brug for en måde kun at udføre kommandoen break
under nogle bestemte forudsætninger,
f.eks. hvis bruger skriver quit
i stedet for et tal
når programmet spørger til tallene a og b.
En såkaldt if-sætning kan bruges til det formål:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # A simple calculator made as an introduction to programming
while True:
print("Calculate a + b:")
in1 = input("a=")
in2 = input("b=")
if in1 == "quit":
break
a = int(in1)
b = int(in2)
c = a+b
output = "{}+{}={}".format(a,b,c)
print(output)
|
Linje 8 sammenligner indholdet i variablen in1
med stringen "quit"
,
hvis de er ens så udføres linje 9 (break
).
Således lukker programmet nu hvis brugeren skriver quit
når programmet beder om en værdi for a
(dog først efter brugeren også har angivet en værdi for b).
Udfordring
Omstruktruer koden således programmet lukker så snart brugeren har skrevet quit og trykket [Enter].
Udvid programmet således programmet lukker uanset om brugeren angiver a eller b til quit.
Udfordring
Prøv at placere if-sætningen efter en af linjerne
som konvertere in1
og in2
til integers.
Hvad sker der nu?
3.1.6. Arbejdsmetoden: Trinvis Forbedring¶
Måske har du lagt mærke til, at vi indtil nu har lavet mange forskellige udgaver af lommeregnerprogrammet som gradvist kan mere og mere, og som gradvist ligner eksemplet vist i starten mere og mere. Det er en meget væsentlig pointe, at programmer ikke skrives i deres fulde endelig version uden at blive testet og omstruktureret under vejs.
figur 3.1, en video af et lommeregner program i brug, er vores mål eller nogle andres krav (engelsk: requirement) til os. Videoen er en optagelse af et Python-program i brug, men den kunne lige så vel være konstrueret i et tegneprogram, eller videoen kunne erstattes af en tekstbeskrivelse, af en person som ønsker sig et lommeregner program, men som ikke selv er i stand til at programmere det. Ud fra kravet eller kravene kan vi opstille nogle specifikationer, f.eks.:
Brugeren skal kunne vælge en regneoperation og operander (tal) til regneoperationen.
Programmet skal printe et letlæseligt resultat af regneoperationen.
Når brugen har udført en beregning skal brugeren tilbydes at lave en ny beregning.
Programmet skal vise en letforståelig fejlmeddelelse når/hvis brugeren angiver en ugyldig operand, f.eks. skriver ordet hest i stedet for et tal når brugeren bedes angive a eller b til udregning a+b.
Nogle af specifikationerne beskriver ikke præcist kravene, f.eks. viser figur 3.1 at brugeren skal kunne vælge regneoperation ved at skrive et tal angivet i en oversigt over regneoperationerne, men det fremgår ikke af specifikationerne.
Kildekode 3.1, Kildekode 3.7 og Kildekode 3.9 med flere kaldes implementeringer af (lommeregner)specifikationerne. Ganske som nogle af specifikationerne ikke præcist beskriver kravene, så opfylder nogle af implementeringerne ikke specifikationerne, f.eks. opfylder implementering Kildekode 3.11 specifikation 2 og 3, men kun dele af 1 og slet ikke 4.
Vores udviklingsprocess eller arbejdsprocess består altså af en række skridt eller delprocesser som gentages indtil vi løber tør for tid eller tilfreds med programmet:
Omdannelse af krav til specifikationer.
Implementing af specifikationerne.
Test af implementeringerne (tjek programmet kan køre/afvikles og overholder specifikationerne).
Forbedring af specifikationer som ikke præcist beskriver kravene.
Forbedring af implementeringer som ikke overholder specifikationerne.
Udviklingsprocessen eller arbejdsmetoden kaldes trinvis forbedring (engelsk: stepwise improvement). Det er en iterativ process fordi de samme skridt eller delprocesser gentages igen og igen.
Udfordring
Lav flere specifikationer (end dem som allerede nævnes i teksten) ud fra kravet/kravene.
Find flere specifikationer (end dem som allerede nævnes i teksten) som bør forbedres for at beskrive kravene mere præcist.
Find flere eksemler (end dem som nævnes i teksten) på implementeringer (fra kapitlet her) som ikke overholder en eller flere specifikationer.
3.1.7. Håndtering af fejl¶
Prøv at afvikle programmet fra Kildekode 3.13 og angiv enten a eller b til et ord som syv eller hest. Du vil da se programmet stoppe med en fejl som denne:
1 2 3 4 | Traceback (most recent call last):
File "<filename>", line <line number>, in <module>
a = int(in1)
ValueError: invalid literal for int() with base 10: 'hest'
|
Som det er krav og som en af vores specifikationer lyder
skal denne brugerfejl, som resulterer i en teknisk fejl,
håndteres og brugeren se en letlæselig fejlbesked.
Dette kan gøres vha. en såkaldt try-except konstruktion.
Vi ved fra de fejlmeddelelser vi har set,
at det er linjerne som konvertere brugerens input til heltal,
linjer hvor funktionen int()
kaldes,
som er årsag til fejlen.
Derfor placeres de linjer i try-except konstruktionen.
Linje 11-15 skal læses som:
prøv at udføre linje 12 og 13,
hvis en af de linjer resultere i en fejl af typen ValueError
så udfør linje 15.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # A simple calculator made as an introduction to programming
while True:
print("Calculate a + b:")
in1 = input("a=")
in2 = input("b=")
if in1 == "quit":
break
try:
a = int(in1)
b = int(in2)
except ValueError:
print("Error: a or b was an invalid number")
c = a+b
output = "{}+{}={}".format(a,b,c)
print(output)
|
1 2 3 4 5 6 7 8 | Calculate a + b:
a=2
b=hest
Error: a or b was an invalid number
Traceback (most recent call last):
File "calc7.py", line 17, in <module>
c = a+b
NameError: name 'b' is not defined
|
Som ses fra outputtet har vi lavet en implementering som opfylder en specifikation, men som nu gør vi ikke længere opfylder en anden specifikation: brugeren ser nu en let læselig fejlmeddelelse hvis der indtastes noget som ikke er et tal, men brugeren ser en anden svært læselig fejlmeddelse og brugeren får ikke mulighed for at udføre en ny beregning (prøve igen).
Den nye fejlmeddelse, af typen NameError,
kommer fordi variablen b
som forsøges oprettet i linje 13
ikke bliver oprettet (den fejler med ValueError).
Når programmet fortsætter efter try-except konstruktionen
så forsøges linje 17 udført og her skal variablen b
,
som nu ikke er defineret,
bruges.
Problemet med der opstår en ny fejl kan håndteres på mange måder,
en af dem kunne være vha. break
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # A simple calculator made as an introduction to programming
while True:
print("Calculate a + b:")
in1 = input("a=")
in2 = input("b=")
if in1 == "quit":
break
try:
a = int(in1)
b = int(in2)
except ValueError:
print("Error: a or b was an invalid number")
break
c = a+b
output = "{}+{}={}".format(a,b,c)
print(output)
|
Denne løsning bryder dog stadig med en af specifikationerne, nemlig den at brugeren skal tilbydes at udføre en ny udregning. Nu ser vi ganske vist ikke ValueError-fejlen, men programmet stopper.
Løsningen er at bytte break
ud med continue
som betyder stop nuværende iteration af loopet
og fortsæt til den næste:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # A simple calculator made as an introduction to programming
while True:
print("Calculate a + b:")
in1 = input("a=")
in2 = input("b=")
if in1 == "quit":
break
try:
a = int(in1)
b = int(in2)
except ValueError:
print("Error: a or b was an invalid number")
continue
c = a+b
output = "{}+{}={}".format(a,b,c)
print(output)
|
Udfordring
Udvid programmet fra Kildekode 3.17 således der gives en særskilt fejlmeddelse for fejlindtastning ved a og b.
Udfordring
Brug str.format()
metoden til at
få fejlmeddelelsen til at se ud som
Error choosing operand 'a', your value 'hest' is not a valid number.
Fodnoter
- 1
Tegnet
#
hedder ikke et hashtag. På engelsk hedder det hash (symbol), number sign eller pound sign, på dansk hedder det havelåge. eller dobbeltkryds.