Skip to content

Commit 834bdee

Browse files
committed
added type hints
1 parent 098d21f commit 834bdee

File tree

3 files changed

+123
-9
lines changed

3 files changed

+123
-9
lines changed

bibliography/bibliography.bib

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ @string { winter
2222
%%authors
2323
@string { a_abadi_marin = "Mart{\'i}n Abadi" }
2424
@string { a_abbasi_hameer = "Hameer Abbasi" }
25+
@string { a_ali_karim = "Karim Ali" }
2526
@string { a_antiga_luca = "Luca Antiga" }
2627
@string { a_archibald_anne_m = "Anne M.\ Archibald" }
2728
@string { a_arthur_jones = "Arthur Jones" }
@@ -129,6 +130,7 @@ @string { a_konovalov_alexander
129130
@string { a_krueger_richard = "Richard Krueger" }
130131
@string { a_kudlur_manjunath = "Manjunath Kudlur" }
131132
@string { a_landau_charles = "Charles Landau" }
133+
@string { a_langa_lukasz = "{\L}ukasz Langa" }
132134
@string { a_larochelle_hugo = "Hugo Larochelle" }
133135
@string { a_larson_eric = "Eric Larson" }
134136
@string { a_laxalde_denis = "Denis Laxalde" }
@@ -192,10 +194,12 @@ @string { a_ribeiro_antonio_h
192194
@string { a_robertson_edmund_f = "Edmund F.\ Robertson" }
193195
@string { a_roscoe_timothy = "Timothy Roscoe" }
194196
@string { a_rosenblatt_bill = "Bill Rosenblatt" }
197+
@string { a_roth_ori = "Ori Roth" }
195198
@string { a_russel_stuart = "Stuart J.\ Russell" }
196199
@string { a_sachsenberg_timo = "Timo Sachsenberg" }
197200
@string { a_sagher_yoram = "Yoram Sagher" }
198201
@string { a_salakoski_tapio = "Tapio Salakoski" }
202+
@string { a_salvaneschi_guido = "Guido Salvaneschi" }
199203
@string { a_scipy_1 = "{{SciPy 1.0 Contributors}}" }
200204
@string { a_shalev_shwartz_shai = "Shai Shalev{-}Shwartz" }
201205
@string { a_shen_kangshen = "Shen Kangshen" }
@@ -266,6 +270,7 @@ @string { l_canada_oakville
266270
@string { l_canada_toronto = "{{Toronto}, {ON}, {Canada}}" }
267271
@string { l_canada_vancouver = "{{Vancouver}, {BC}, {Canada}}" }
268272
@string { l_china_beijing = "{{China}, {Beijing}}" }
273+
@string { l_germany_wadern = "{{Wadern}, {Saarland}, {Germany}}" }
269274
@string { l_portugal_lisbon = "{{Lisbon}, {Portugal}}" }
270275
@string { l_spain_leioa = "{{Leioa}, {Bizkaia}, {Spain}}" }
271276
@string { l_switzerland_cham = "{{Cham}, {Switzerland}}" }
@@ -319,6 +324,7 @@ @string { p_ieee
319324
@string { p_infinite_skills = "Infinite Skills Inc" }
320325
@string { p_informs = "{The Institute for Operations Research and the Management Sciences~({INFORMS})}" }
321326
@string { p_iso = "{International Organization for Standardization~{(ISO)}}" }
327+
@string { p_leibniz_zentrum_fur_informatik = "{{Schloss Dagstuhl} -- {Leibniz-Zentrum f{\"u}r Informatik}}" }
322328
@string { p_manning_publications = "{Manning Publications}" }
323329
@string { p_mit_press = "{{MIT} Press}" }
324330
@string { p_neurips = "{The Neural Information Processing Systems Foundation~{(NeurIPS)}}" }
@@ -362,6 +368,7 @@ @string { pa_ieee
362368
@string { pa_ieee_ny = l_usa_new_york }
363369
@string { pa_infinite_skills = l_canada_oakville }
364370
@string { pa_informs = l_usa_catonsville }
371+
@string { pa_leibniz_zentrum_fur_informatik = l_germany_wadern }
365372
@string { pa_manning_publications = l_usa_shelter_island }
366373
@string { pa_mit_press = l_usa_cambridge }
367374
@string { pa_neurips = l_usa_san_diego }
@@ -551,6 +558,13 @@ @xdata{ser_isor
551558
issn = {0884-8289},
552559
}
553560

561+
@xdata{ser_lipics,
562+
series = {Leibniz International Proceedings in Informatics~{(LIPIcs)}},
563+
issn = {1868-8969},
564+
publisher = p_leibniz_zentrum_fur_informatik,
565+
address = pa_leibniz_zentrum_fur_informatik
566+
}
567+
554568
@xdata{ser_u,
555569
series = {Universitext~{(UTX)}},
556570
issn = {0172-5939},
@@ -569,7 +583,6 @@ @inproceedings{ABCCDDDGIIKLMMMSTVWWYZ2016TASFLSML
569583
title = {\tensorflow: {A} System for Large-Scale Machine Learning},
570584
crossref = {PROC2016OSDI},
571585
pages = {265--283},
572-
isbn = {978-1-931971-33-1},
573586
url = {https://www.usenix.org/conference/osdi16/technical-sessions/presentation/abadi},
574587
urldate = {2024-06-26}
575588
}
@@ -950,8 +963,7 @@ @book{L2023TDDBTADMLMWT
950963
title = {\tensorflow\ Deep Dive: Build, Train, and Deploy Machine Learning Models with \tensorflow},
951964
date = {2023-12},
952965
publisher = p_oreilly,
953-
address = pa_oreilly,
954-
isbn = {0636920924425},
966+
address = pa_oreilly
955967
}
956968

957969
@book{L2024PW,
@@ -1067,6 +1079,16 @@ @techreport{PEP257
10671079
urldate = {2024-07-27},
10681080
}
10691081

1082+
@techreport{PEP484,
1083+
title = {Type Hints},
1084+
author = a_van_rossum_guido # and # a_langa_lukasz,
1085+
number = {484},
1086+
xdata = {rep_pep},
1087+
date = {2014-09-29},
1088+
url = {https://peps.python.org/pep-0484},
1089+
urldate = {2024-08-22},
1090+
}
1091+
10701092
@techreport{PEP498,
10711093
author = a_smith_eric_v,
10721094
title = {Literal String Interpolation},
@@ -1134,6 +1156,15 @@ @proceedings{PROC2019NEURIPS
11341156
isbn = {9781713807933}
11351157
}
11361158

1159+
@proceedings{PROC2023ECOOP,
1160+
title = {37th European Conference on Object-Oriented Programming~{(ECOOP'23)}, } # jul # {~17-21, 2023, } # l_usa_seattle,
1161+
editor = a_ali_karim # and # a_salvaneschi_guido,
1162+
xdata = {ser_lipics},
1163+
volume = {263},
1164+
date = {2023-07-11},
1165+
isbn = {978-3-95977-281-5},
1166+
}
1167+
11371168
@proceedings{PROC2023GECCO,
11381169
xdata = {c_gecco},
11391170
booktitle = {Companion Proceedings of the Conference on Genetic and Evolutionary Computation~{(GECCO'23)}, Companion Volume, } # jul # {~15-19, 2023, } # l_portugal_lisbon,
@@ -1225,6 +1256,14 @@ @article{PVGMTGBPWDVPCBPD2011SMLIP
12251256
doi = {10.5555/1953048.2078195},
12261257
}
12271258

1259+
@inproceedings{R2023PTHATCPBNI,
1260+
author = a_roth_ori,
1261+
title = {\python\ Type Hints Are Turing Complete (Pearl/Brave New Idea)},
1262+
crossref = {PROC2023ECOOP},
1263+
pages = {44:1--44:15},
1264+
doi = {10.4230/LIPICS.ECOOP.2023.44},
1265+
}
1266+
12281267
@book{RLM2022MLWPAS,
12291268
author = a_raschka_sebastian # and # a_liu_yuxi # and # a_mirjalili_vahid,
12301269
title = {Machine Learning with \pytorch\ and \scikitlearn},

text/main/basics/simpleDataTypesAndOperations/int/int.tex

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@
5656
Computing \pythonil{32 / 4} thus yields~\pythonil{8.0}, \pythonil{33 / 4} gives us \pythonil{8.25}, \pythonil{34 / 4} yields~\pythonil{8.5}, \pythonil{35 / 4} results in \pythonil{8.75}, and, finally, \pythonil{36 / 4} returns~\pythonil{9.0}.
5757
Notice that the result of this division operator is always a floating point number, even if the number itself is an integer.
5858

59-
\bestPractice{intDivision}{Always be careful with which division operator you use for \pythonilIdx{int}s. If you need an integer result, make sure to use \pythonilIdx{//}. Remember that \pythonilIdx{/} always returns a \pythonilIdx{float} (and see \cref{bp:floatImprecise}), even if the result is a whole number.}
59+
\bestPractice{intDivision}{Always be careful with which division operator you use for \pythonilIdx{int}s. %
60+
If you need an integer result, make sure to use \pythonilIdx{//}. %
61+
Remember that \pythonilIdx{/} always returns a \pythonilIdx{float} (and see \cref{bp:floatImprecise}), even if the result is a whole number.}
6062

6163
Now above we have said that \pythonil{33 // 4} yields the integer~\pythonil{8}.
6264
The remainder of this operation can be computed using the \pgls{modulodiv} operator \expandafter\pythonilIdx{\%}, i.e., by typing \pythonil{33 \% 4}, which yields~\pythonil{1}.
@@ -121,7 +123,7 @@
121123

122124
In \python, we can compute the bitwise (i.e., binary) \emph{or}, \emph{and}, as well as the and \emph{exclusive~or} of this binary representation of integers using the \pythonil{|}, \pythonil{&}, and \pythonil{^} operators, respectively.
123125
Binary \emph{or} returns an integer in which all bits are set to~1 which were~1 in either of its two operands.
124-
\pythonil{22 | 1}\pythonIdx{|} yields \pythonil{23}, because the bit with value~1 is not set in~22 and the binary~\emph{or} sets it (effectively adding~1 to~22).
126+
\pythonil{22 | 1}\pythonIdx{|!bit-wise or} yields \pythonil{23}, because the bit with value~1 is not set in~22 and the binary~\emph{or} sets it (effectively adding~1 to~22).
125127
The slightly more comprehensive example \pythonil{22 | 15} sketched in \cref{fig:binaryMath} gives us \pythonil{31}, because $22=2^4+2^2+2^1$ and $15=2^3+2^2+2^1+2^0$, which \pythonil{|} combines to $31=2^4+2^3+2^2+2^1+2^0$, i.e., each power of~2 that occurred in either of the two operands is present in the result.
126128
\pythonil{bin(31)} yields \pythonil{'0b11111'}.
127129

text/main/basics/variables/typesAndTypeHints/typesAndTypeHints.tex

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
This is not forbidden, but when reading the code, it must strike you as odd.
4242

4343
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}).
4545
Maybe they wanted to do an integer division and accidentally did a floating point division.
4646
Depending on what the code later on (in our imaginary larger program) does, it could be very hard to find such an error.
4747

@@ -50,6 +50,9 @@
5050
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}.
5151
This could again lead to all sorts of strange errors later on in her code.
5252

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{//}.
5356
Regardless of what is true, you will certainly agree that something is wrong with this program.
5457
And most certainly, it was just a small oversight, maybe even just a typo.
5558
Unfortunately, since you are not the author of the program, you do not know what is wrong.
@@ -145,12 +148,82 @@
145148
Remember that \inQuotes{real programs} are much more complex than \cref{lst:variables:types_wrong}.
146149
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.
147150

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}.
149152
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.}%
151165
%
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.%
153224
%
154225
\FloatBarrier%
155226
\endhsection%
156227
%
228+
\endhsection%
229+
%

0 commit comments

Comments
 (0)