|
41 | 41 | This is not forbidden, but when reading the code, it must strike you as odd.
|
42 | 42 |
|
43 | 43 | Indeed, there are at least two possible explanations for this:
|
44 |
| -On the one hand, maybe, the original author of this code mistakenly mixed up the \pythonilIdx{/} operator for the \pythonilIdx{//}. |
| 44 | +On the one hand, maybe, the original author of this code mistakenly mixed up the \pythonilIdx{/} operator for the \pythonilIdx{//} (see \cref{bp:intDivision}). |
45 | 45 | Maybe they wanted to do an integer division and accidentally did a floating point division.
|
46 | 46 | Depending on what the code later on (in our imaginary larger program) does, it could be very hard to find such an error.
|
47 | 47 |
|
|
50 | 50 | What if another programmer continues to work on this code and, based on the variable's name, expects it to contain an \pythonil{int} whereas it actually contains a \pythonil{float}.
|
51 | 51 | This could again lead to all sorts of strange errors later on in her code.
|
52 | 52 |
|
| 53 | +\bestPractice{codeClarity}{The names we use in program code should clearly reflect our intentions.} |
| 54 | + |
| 55 | +If the programmer followed \cref{bp:codeClarity}, then the code would clearly contain a bug, namely a mix-up between the \pythonilIdx{/} operator for the \pythonilIdx{//}. |
53 | 56 | Regardless of what is true, you will certainly agree that something is wrong with this program.
|
54 | 57 | And most certainly, it was just a small oversight, maybe even just a typo.
|
55 | 58 | Unfortunately, since you are not the author of the program, you do not know what is wrong.
|
|
145 | 148 | Remember that \inQuotes{real programs} are much more complex than \cref{lst:variables:types_wrong}.
|
146 | 149 | Imagine wading through thousands of lines of code to figure out what type a variable has, and, while doing so, remember that \python\ permits overwriting the contents of a variable with objects of an entirely different type whenever it pleases us.
|
147 | 150 |
|
148 |
| -Realizing that dynamic typing can be a blessing but also a problem, \emph{optional} type hints were introduced into the \python\ language. |
| 151 | +Realizing that dynamic typing can be a blessing but also a problem, \emph{optional} type hints were introduced into the \python\ language~\cite{PEP484,R2023PTHATCPBNI}. |
149 | 152 | We can now declare the type of a variable if we want.
|
150 |
| -This solves the above problem basically entirely and allows us to tell type checking tools our intention.% |
| 153 | +This solves the above problem basically entirely and allows us to tell type checking tools our intention. |
| 154 | + |
| 155 | +When we declare and assign a variable, \pythonil{my_var = 1} for example, and want to define it as integer variable, for example, we simply write \pythonil{my_var: int = 1}. |
| 156 | +Of course, a type checker will already see that \pythonil{my_var} should be an integer variable when we assign the integer~\pythonil{1} to it. |
| 157 | +However, by writing \pythonil{: int}\pythonIdx{:} after its name, we also clearly establish our intention. |
| 158 | +And this makes a difference. |
| 159 | + |
| 160 | +\gitPythonAndOutput{\programmingWithPythonCodeRepo}{01_variables}{variable_types_wrong_hints_1.py}{--args format}{variables:types_wrong_hints_1}{% |
| 161 | +\cref{lst:variables:types_wrong}, but with the variable explicitly hinted as \pythonil{int}.}% |
| 162 | +%% |
| 163 | +\gitPythonAndOutput{\programmingWithPythonCodeRepo}{01_variables}{variable_types_wrong_hints_2.py}{--args format}{variables:types_wrong_hints_2}{% |
| 164 | +\cref{lst:variables:types_wrong}, but with the variable explicitly hinted as either \pythonil{int} or \pythonil{float} and named appropriately.}% |
151 | 165 | %
|
152 |
| -\endhsection% |
| 166 | +\gitOutput{\programmingWithPythonCodeRepo}{.}{scripts/mypy.sh 01_variables variable_types_wrong_hints_1.py}{variables:variable_types_wrong_hints_1:mypy}{% |
| 167 | +The results of static type checking with \mypy\ of the program given in \cref{lst:variables:types_wrong_hints_1}.}% |
| 168 | +% |
| 169 | +\gitOutput{\programmingWithPythonCodeRepo}{.}{scripts/mypy.sh 01_variables variable_types_wrong_hints_2.py}{variables:variable_types_wrong_hints_2:mypy}{% |
| 170 | +The results of static type checking with \mypy\ of the program given in \cref{lst:variables:types_wrong_hints_2}.}%% |
| 171 | + |
| 172 | +If the author of \cref{lst:variables:types_wrong} had used type hints, they could have written their program differently, as illustrated in \cref{lst:variables:types_wrong_hints_1,lst:variables:types_wrong_hints_2}. |
| 173 | +Both programs as well as the original one produce exactly the same output if we execute them with the \python\ interpreter, since type hints are ignored by the interpreter. |
| 174 | +However, if we type-check them with \mypy, we get two different results, namely \cref{exec:variables:variable_types_wrong_hints_1:mypy,exec:variables:variable_types_wrong_hints_2:mypy}, respectively. |
| 175 | + |
| 176 | +In \cref{lst:variables:types_wrong_hints_1}, we annotate \pythonil{int_var} as an integer variable by writing \pythonil{int_var: int}. |
| 177 | +While the name of the variable already indicates that we want it to hold integers, we now have formally specified this intention. |
| 178 | +The \mypy\ type checker tells is in \cref{exec:variables:variable_types_wrong_hints_1:mypy} basically the same as it stated In \cref{exec:variables:variable_types_wrong:mypy}, namely that assigning a \pythonil{float} to this variable is wrong. |
| 179 | + |
| 180 | +However, if the intention of the author wanted that the variable could hold either an \pythonil{int} or a \pythonil{float}, they could have annotated with the type hint \pythonil{int | float}. |
| 181 | +The \pythonil{|}\pythonIdx{|!type hints} here is not the \inQuotes{bit-wise or} we learned back in \cref{sec:int:bitstrings}, but instead indicates that both \pythonils{int} and \pythonils{float} are acceptable values for a variable.\footnote{% |
| 182 | +Technically speaking, the type hint specification in PEP484~\cite{PEP484} allows all variables annotated with \pythonil{float} to also accept \pythonil{int} values in section \href{https://peps.python.org/pep-0484/\#the-numeric-tower}{The Numeric Tower}. % |
| 183 | +Thus annotating the variable only with \pythonil{: float} would have been sufficient. % |
| 184 | +However, since \pythonil{int} is actually not a subclass of \pythonil{float} in \python, this can be confusing in some cases. % |
| 185 | +Writing \pythonil{int | float} gives me the opportunity to introduce the \pythonil{|}~operator {\dots} so this is how we do it.% |
| 186 | +} |
| 187 | +This annotation is used in \cref{lst:variables:types_wrong_hints_2}. |
| 188 | +A side-effect of using this type is that, when writing this code, its author would have realized that the name \pythonil{int_var} would not be an appropriate name. |
| 189 | +They would probably have used something like \pythonil{my_var} instead. |
| 190 | +Applying \mypy\ to this program yields the output \cref{exec:variables:variable_types_wrong_hints_2:mypy}, which indicates that everything is OK. |
| 191 | + |
| 192 | +Using type hints together with type checking would have prevented the problems in \cref{lst:variables:types_wrong} from the start. |
| 193 | +Either, its author would have discovered the mistake of computing a \pythonil{float} value instead of an \pythonil{int}. |
| 194 | +Or they would have more clearly communicated their intention by marking the variable to either hold an \pythonil{int} or a \pythonil{float}. |
| 195 | +And this comes at no cost at all when executing the program, because type hints are only checked by programmers and tools, but not by the interpreter. |
| 196 | +% |
| 197 | +\bestPractice{typeHints}{Always use type hints.}% |
| 198 | +% |
| 199 | +\gitPython{\programmingWithPythonCodeRepo}{01_variables/variable_types_hints.py}{--args format}{variables:types_hints}{% |
| 200 | +A variant of \cref{lst:variables:types} which has been improved by adding type annotations.}% |
| 201 | +% |
| 202 | +\gitOutput{\programmingWithPythonCodeRepo}{.}{scripts/mypy.sh 01_variables variable_types_hints.py}{variables:variable_types_hints:mypy}{% |
| 203 | +The results of static type checking with \mypy\ of the program given in \cref{lst:variables:types_hints}.} |
| 204 | + |
| 205 | +For the sake of completeness, let us also annotate \cref{lst:variables:types} with type hints as a small exercise. |
| 206 | +The variable \pythonil{int_var}, in which we want to store the integer value~\pythonil{8}, will be annotated with \pythonil{: int}. |
| 207 | +The variable \pythonil{float_var}, in which we want to store the floating point number~\pythonil{3.0}, will be annotated with \pythonil{: float}. |
| 208 | +The variable \pythonil{str_var}, in which we want to store the string~\pythonil{"float_var=3.0"}, will be annotated with \pythonil{: str}. |
| 209 | +The variable \pythonil{bool_var}, in which we want to store the value~\pythonil{False}, will be annotated with \pythonil{: bool}. |
| 210 | +And, finally, the variable \pythonil{none_var}, in which we want to store \pythonil{None}, will be annotated with \pythonil{: None}. |
| 211 | +This produces \cref{lst:variables:types_hints}, which we can check with \mypy\ and obtain \cref{exec:variables:variable_types_hints:mypy}. |
| 212 | + |
| 213 | +It is very obvious at this point that including the type of the variable in the variable name is no needed. |
| 214 | +It is not helpful for any tool. |
| 215 | +If we want to convey our intention about the type to another programmer who may be reading our code, then type hints are a much better choice. |
| 216 | +Different from variable names, they also can be interpreted by type checkers. |
| 217 | + |
| 218 | +With type hints, we have brought the advantages of a statically typed programming language to \python. |
| 219 | +Since they are optional, a programmer can choose whether and where to use them. |
| 220 | +However, if you are a student attending one of my courses, consider them as mandatory. |
| 221 | +Their advantage is that they allow us to find many (though by far not all) logical errors. |
| 222 | +They make the code easier to read and easier to understand. |
| 223 | +Therefore, from my perspective, \cref{bp:typeHints} \emph{always} applies.% |
153 | 224 | %
|
154 | 225 | \FloatBarrier%
|
155 | 226 | \endhsection%
|
156 | 227 | %
|
| 228 | +\endhsection% |
| 229 | +% |
0 commit comments