-
Notifications
You must be signed in to change notification settings - Fork 0
/
villur.tex
231 lines (197 loc) · 12.6 KB
/
villur.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
\chapterimage{chapters14.png} % Chapter heading image
\chapter{Villur og villumeðhöndlun}\index{Villur og villumeðhöndlun}\label{k:villur}
Hingað til hefur kóðinn okkar hreinlega hætt keyrslu þegar við fáum villur og við þurft að laga eitthvað.
Í kafla \ref{uk:tolur-villur} sáum við upptalningu á þeim helstu villum sem við getum lent í.
Það sem við viljum hins vegar geta gert er að bregðast við villum til þess að forritin okkar haldi áfram keyrslu þrátt fyrir að eitthvað hafi farið úrskeiðis.
Við viljum geta sagt vélinni að reyna að gera eitthvað og ef henni tekst það ekki vegna þess að það myndi valda villu viljum við geta gert eitthvað annað og haldið áfram eða hætt.
\section{Algengar villur}\index{Algengar villur}\label{uk:villur-algengar}
Byrjum á að rifja upp algengar villur og bætum nokkrum við:
\begin{itemize}
\item \textbf{Nafnavilla} - \emph{NameError}, breytunafn er notað sem hefur ekki verið skilgreint.
\item \textbf{Inndráttarvilla }- \emph{IndentationError}, röngum inndrætti beitt.
\item \textbf{Málskipanarvilla} - \emph{SyntaxError}, rangt tákn notað eða tákn notað vitlaust.
\item \textbf{Týpuvilla} - \emph{TypeError}, týpan styður ekki aðgerðina sem er verið að framkvæma.
\item \textbf{Vísisvilla} - \emph{IndexError}, verið er að nota sætisvísi sem er ekki til í hlutnum.
\item \textbf{Gildisvilla} - \emph{ValueError}, verið er að nota gildi sem er ekki til.
\item \textbf{Eigindavilla} - \emph{AttributeError}, verið er að nota eigindi sem hluturinn á ekki til.
\item \textbf{Lyklavilla} - \emph{KeyError}, verið er að ná í lykil sem er ekki til.
\item \textbf{Endurkvæmnisvilla} - \emph{RecursionError}, þegar búið er að ná hámarks leyfilegri endurkvæmni án niðurstöðu.
\item \textbf{Staðvær nafnavilla} - \emph{UnboundLocalError}, þegar verið er að vísa í staðvært breytuheiti en það hefur ekki verið skilgreint á þeim stað í gildissviðinu.
\item \textbf{Inntaks/úttaksvilla} - \emph{IOError}, þegar villa kemur upp við meðhöndlun inntaks eða úttaks.
\end{itemize}
\vspace{0.5cm}
Ástæðan fyrir því að nefna nákvæmlega þessar villur en ekki allar sem eru skráðar í skjölun Python forritunarmálsins er sú að þessar villur eru líklegri en aðrar til að koma upp hjá byrjendum og við viljum geta tekið á þeim.
Inndráttarvillur og málskipanarvillur er þó ekki hægt að grípa á keyrslutíma því þær eru gripnar áður en keyrsla á sér stað og kóðinn hreinlega keyrir ekki neitt.
Ágætt er að hafa í huga að kóðinn okkar þarf að vera réttur og rétt uppsettur til þess að geta keyrt yfirhöfuð.
Hinar villurnar viljum við kannski geta gripið og meðhöndlað svo að við getum haldið áfram með það sem við vorum að gera.
Við viljum ekki að notandinn sé allt í einu læstur úti eða að forritið hætti alfarið keyrslu ef eitthvað minni háttar kemur upp, eins og ef inntakið frá notanda er ekki af réttri týpu eða ekki hægt að kasta því í rétta týpu.
\section{Að grípa villur}\index{Að grípa villur}\label{uk:villur-grípa}
Til þess að grípa villur og meðhöndla þær þurfum við nokkur ný lykilorð.
Þau eru \textbf{try}, \textbf{except}, \textbf{else}, \textbf{finally} eða \textit{reyna}, \textit{nema}, \textit{annars}, \textit{að lokum}.
Við höfum séð else áður og það virkar nokkuð svipað í þessari stöðu.
Það sem try gerir er það sem við viljum reyna á, það sem við höldum að muni valda villu.
Við viljum geta reynt, \texttt{try}, að keyra kóðann, til dæmis kalla á einhverja vefþjónustu eða kasta inntaki frá notanda, án þess að forritið hætti.
Ef kóðinn sem við reyndum að keyra veldur villu getum við gripið hana með \texttt{except} klausu, þannig að við ætlum að reyna að keyra kóða nema ef það virkar ekki viljum við gera eitthvað annað.
Annars, \texttt{else}, ef það virkaði að keyra kóðann getum við gert eitthvað vitandi að það muni ekki valda villu.
Að lokum getum við svo gert eitthvað burtséð frá því hvort það olli villu eða ekki, \texttt{finally} klausan mun alltaf keyrast.
Flæðiritið fyrir þessa hugmynd er nokkuð svipað skilyrðissetningum með \texttt{if}, \texttt{elif} og \texttt{else}.
Það kemur ein \texttt{try} setning, á eftir henni koma eins margar \texttt{except} setningar og við viljum (þar sem hver og ein er þá að taka á einhverri tiltekinni villu), þá má koma ein \texttt{else} setning og að lokum má koma ein \texttt{finally} setning.
Hún keyrist sama hvað og er notuð til þess að framkvæma þá virkni sem verður að eiga sér stað, eins og til dæmis að loka skjali sem verið er að vinna í.
Ástæða þess að það er gagnlegt að vita hvað villurnar heita er að except klausurnar okkar geta gripið ákveðnar villur og ef sú tiltekna villa kemur upp getum við tekið á nákvæmlega því tilfelli.
Í kóðabút \ref{lst:villur-grip-kynnt} sjáum við hvernig á að beita þessum nýju lykilorðum og hvernig uppsetningin á þeim þarf að vera.
Gerum ráð fyrir að breytan \texttt{tala} sé sett sem eitthvað eins og \# í stað skiljanlegrar tölu.
Prófið ykkur áfram með það.
\begin{lstlisting}[caption=Hvernig á að beita try\, except og else, label=lst:villur-grip-kynnt]
tala = input('veldu tölu ')
try:
tala = int(tala)
except:
print('þú gafst ekki upp neitt sem mátti túlka sem tölu')
tala = 0 # notum þá bara eitthvað annað gildi
print('talan sem þú ert með er', tala)
tala = input('veldu tölu: ')
try:
int(tala)
except TypeError:
print('ekki gekk að kasta í tölu út af týpuvillu')
except ValueError:
print('ekki gekk að kasta í tölu út af gildisvillu')
except AttributeError:
print('ekki gekk að kasta í tölu út af eigindavillu')
except:
print('ekki gekk að kasta út af einhverri annarri villu sem ekki er reynt að grípa sérstaklega')
else:
print('það gekk bara víst að kasta í tölu')
\end{lstlisting}
\lstset{style=uttak}
\begin{lstlisting}
þú gafst ekki upp neitt sem mátti túlka sem tölu
talan sem þú ert með er 0
veldu tölu: #
ekki gekk að kasta í tölu út af gildisvillu
\end{lstlisting}
\lstset{style=venjulegt}
Í fyrri hlutanum er það gripið ef einhver villa á sér stað en í seinni hlutanum er tekið á þremur mismunandi tilfellum áður en gefist er upp.
Þetta gæti verið gott þegar þrjár tilteknar villur eru líklegar og þörf er á að taka á þeim.
Líklega er best að hafa \texttt{try} klausurnar hnitmiðaðar, nota þær til að taka á líklegum villum sem gætu komið upp á viðkvæmum stöðum.
Ekki er ráðlagt að setja svona klausur í öll föll og allar aðferðir til þess að tryggja að ekkert fari nokkurn tímann úrskeiðis.
Það myndi taka óþarflega langan tíma í útfærslu að greina hvaða villur þarf að grípa hverju sinni og hvernig.
Það myndi ekki tryggja að forritið okkar væri öruggara.
Viðkvæmir staðir eru til dæmis þar sem tekið er við gögnum frá notendum eða þegar gögn eru birt notendum.
Við viljum tryggja það að notendur geti ekki gefið okkur skaðleg gögn.\footnote{\href{https://en.wikipedia.org/wiki/SQL_injection}{Hlekkur á SQL injection árasir á ensku Wikipediu.}}
\begin{lstlisting}[caption=Hvernig á að beita try - except - else, label=lst:villur-grip-kynnt-2]
try:
int('strengur')
except TypeError:
print('hér er tekið á villu sem á sér ekki stað')
except:
print('hér er tekið á öllum öðrum villum, ef þessari klausu er sleppt munum við ekki grípa neina villu því þetta er vissulega ekki týpuvilla')
else:
print('það er ljóst að við förum ekki hingað inn því kóðinn veldur villu')
finally:
print('við förum alltaf hér inn sama hvað, hvort sem try virkaði eða ekki, jú nema við höfum gleymt að grípa villuna og forritið hætti keyrslu')
\end{lstlisting}
\lstset{style=uttak}
\begin{lstlisting}
hér er tekið á öllum öðrum villum, ef þessari klausu er sleppt munum við ekki grípa neina villu því þetta er vissulega ekki týpuvilla
við förum alltaf hér inn sama hvað, hvort sem try virkaði eða ekki, jú nema við höfum gleymt að grípa villuna og forritið hætti keyrslu
\end{lstlisting}
\lstset{style=venjulegt}
Þegar við erum að reyna að grípa svona margar villur eins og í kóðabút \ref{lst:villur-grip-kynnt} er það vegna þess að við erum ekki viss hvað það er sem mun fara úrskeiðis.
En \texttt{try} klausan okkar er tiltölulega einföld og því fátt sem kemur til greina sem gæti farið úrskeiðis, en við gætum verið að reyna á margt í einu og því gagnlegt að vita af því að við getum gripið margar villur á einu bretti.
Annar möguleiki sem við viljum geta reynt á er að hreiðra klausurnar okkar þannig að ef við reynum eitthvað sem gengur ekki viljum við grípa það en reyna svo eitthvað annað.
Þar kemur einnig sterkt inn að vita hvað villurnar okkar heita svo við getum reynt eitthvað ákveðið byggt á því hvaða villu við fengum.
Í kóðabút \ref{lst:villur-grip-hreiðrað} sjáum við hvernig hægt er að halda áfram að reyna að kasta inntaki þegar það gengur ekki við fyrstu tilraun.
%\begin{wrapfigure}{i}{0.2\textwidth} %i o r l
\begin{center}
\includegraphics[scale=1.6]{doodles31-15.png}
\end{center}
%\end{wrapfigure}
\begin{lstlisting}[caption=Hvernig má hreiðra try\, except og else, label=lst:villur-grip-hreiðrað]
try:
tala = input('skrifaðu tölustaf ')
tala = int(tala)
except:
try:
tala = int(tala[0])
except:
print('þú skrifaðir ekki tölu sem hægt var að skilja')
tala = 0
print('talan var', tala)
\end{lstlisting}
\lstset{style=uttak}
\begin{lstlisting}
skrifaðu tölustaf fimmtán
þú skrifaðir ekki tölu sem hægt var að skilja
talan var 0
\end{lstlisting}
\lstset{style=venjulegt}
\phantom{easter egg}
Við viljum temja okkur að taka á villum.
Við viljum heldur sjá snyrtileg villuskilaboð sem eru lýsandi fyrir vandamálið en ekki stafasúpu af torskildum tækniupplýsingum.
Þetta nýtist t.d. ef við erum með forrit sem er brothætt og við viljum að notendur skilji hvað fór úrskeiðis eða ef við erum með vél sem keyrir endalaust (eins og hitamælir á pallinum) sem sendir okkur svo tölvupóst ef hún lendir í villu.
\comment{
\subsection{Að meðhöndla eigin villur}\index{Að meðhöndla eigin villur}\label{uk:villur-raise}
ræða við mér vitrara fólk
}
%-------------------------------ÆFINGAR---------------------%
\newpage
\section{Æfingar}
\begin{exercise}\label{vil1}
Búið til fall sem tekur við einu viðfangi og prentar út hvort það sé stærra eða minna en 0 (True eða False dugar, True fyrir stærra og False fyrir minna).
Athugið að ekki er hægt að bera saman ólík tög og þið þurfið því að villumeðhöndla inntakið.
Ef það er ekki sambærilegt við 0 skulið þið í staðinn prenta 'ekki sambærilegt'.
Athugið sérstaklega fleytitölur.
Ef fleytitölunni 0.1 er kastað í heiltölu verður hún að 0, ef strengnum '0.1' er kastað í heiltölu fæst villa.
Því þurfið þið að vera á varðbergi fyrir mögulegum fleytitölum.
\end{exercise}
\setboolean{firstanswerofthechapter}{true}
\begin{Answer}[ref={vil1}]
Nú þurfum við að vera vakandi fyrir því hvað það er sem notandinn getur sett inn sem tölu, hvað við viljum prenta út hverju sinni og hvernig við prófum að við höfum náð öllum tilvikum.
\begin{lstlisting}
def samanburdur_vid_null(vidfang):
try:
int(vidfang)
except:
try:
float(vidfang)
except:
print("ekki sambærilegt")
else:
print(0 < float(vidfang))
else:
if(type(vidfang) == float):
print(0 < vidfang)
else:
print(0 < int(vidfang))
samanburdur_vid_null("0.1")
samanburdur_vid_null(0.1)
samanburdur_vid_null("3")
samanburdur_vid_null([1])
samanburdur_vid_null("fimm")
samanburdur_vid_null({"a":1})
samanburdur_vid_null(-23)
\end{lstlisting}
\end{Answer}
\setboolean{firstanswerofthechapter}{false}
\begin{exercise}\label{vil2}
Skrifið kóða sem grípur eftirfarandi villur:
\begin{tasks}(2)
\task\label{vil2-a} Nafnavilla.
\task\label{vil2-b} Eigindavilla.
\task\label{vil2-c} Lyklavilla.
\task\label{vil2-d} Gildisvilla.
\end{tasks}
\end{exercise}
\begin{Answer}[ref={vil2}]
Hér eru kóðastubbar sem valda eftirfarandi villum, fleiri leiðir eru til að fá villurnar.
Svarið í a. lið er að því gefnu að breytan \texttt{bolti} hafi aldrei verið skilgreind áður.
Hér er það í höndum lesenda að skrifa svo kóðann sem grípur villurnar.
\begin{tasks}
\task \texttt{bolti}
\task \texttt{"halló".sort()}
\task \texttt{{}["a"]}
\task \texttt{int("vidfang")}
\end{tasks}
\newpage
\end{Answer}