Skip to content

Commit 50de554

Browse files
committed
added text on default arguments
1 parent bba5b53 commit 50de554

File tree

7 files changed

+150
-11
lines changed

7 files changed

+150
-11
lines changed

styles/listing.sty

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ literate=%
6666
{≈}{{$\approx$}}1 {∈}{{$\in$}}1 {∉}{{$\notin$}}1%
6767
{√}{{\raisebox{0.55ex}{$\sqrt{\relax}$}}}1%
6868
{²}{{\textsuperscript{2}}}1{³}{{\textsuperscript{3}}}1%
69+
{∫}{{$\int$}}1%
6970
}%
7071
%
7172
\lstdefinestyle{python_style}{%

text/main/basics/collections/lists/lists.tex

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
The result is a new list which contains all elements of the first list~\pythonil{a} followed by all the elements from the second list~\pythonil{b}.
102102
Therefore, the expression \pythonil{[1, 2, 3, 4] + [5, 6, 7]} results in \pythonil{[1, 2, 3, 4, 5, 6, 7]}.
103103
Similarly, if you multiply a list by an integer~\pythonil{n}, you get a new list which equals the old list \pythonil{n}~times concatenated.
104-
\pythonil{[5, 6, 7] * 3}\pythonIdx{*}\pythonIdx{list!*} therefore yields \pythonil{[5, 6, 7, 5, 6, 7, 5, 6, 7]}.
104+
\pythonil{[5, 6, 7] * 3}\pythonIdx{*!list}\pythonIdx{list!*} therefore yields \pythonil{[5, 6, 7, 5, 6, 7, 5, 6, 7]}.
105105

106106
In \cref{sec:strBasicOperations}, we discussed string slicing.
107107
Lists can be sliced in pretty much the same way\pythonIdx{list!slicing}\pythonIdx{slicing}\pythonIdx{slice}~\cite{PSF2024S}.

text/main/basics/simpleDataTypesAndOperations/float/float.tex

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,15 @@
8787

8888
The normal arithmetic operations like addition, subtraction, multiplication, division, and powers all work with \pythonils{float}\pythonIdx{float} as expected.
8989
Remember, however, that if \pythonilIdx{float} and \pythonilIdx{int} numbers are mixed, the results are always \pythonils{float}\pythonIdx{float}.
90-
Thus, \pythonil{1.0 + 7}\pythonIdx{+} gives us \pythonil{8} and \pythonil{2 * 3.0}\pythonIdx{*} yields \pythonil{6.0}.
90+
Thus, \pythonil{1.0 + 7}\pythonIdx{+} gives us \pythonil{8} and \pythonil{2 * 3.0}\pythonIdx{*!multiplication} yields \pythonil{6.0}.
9191
In other words, if one \pythonilIdx{float} occurs somewhere in an expression, it will \inQuotes{infect} everything that it touches to become a \pythonilIdx{float} too, even if the result could be represented as \pythonilIdx{int}.
9292
Some results cannot be integers anyway, for example \pythonil{5 - 3.6}\pythonIdx{-} evaluates to~\pythonil{1.4}.
9393
The remainder (the \glsdisp{modulodiv}{modulo}) of a division can also be computed for floating point numbers.
9494
The remainder of the division of 6.5 by 2, i.e., \expandafter\pythonil{6.5 \% 2}\pythonIdx{\%} is~\pythonil{0.5}.%
9595
%
9696
\begin{sloppypar}%
9797
The square root of 3.3 can be computed as $3.3^{0.5}$.
98-
We can write this as \pythonil{3.3 ** 0.5}\pythonIdx{**}, which yields \pythonil{1.816590212458495}.
98+
We can write this as \pythonil{3.3 ** 0.5}\pythonIdx{**!power}, which yields \pythonil{1.816590212458495}.
9999
This brings us back to the previous section:
100100
$\sqrt{3.3}$~is not actually 1.816\decSep590\decSep212\decSep458\decSep495.
101101
It is an irrational number~\cite{S1988WPCHD,B1991IWNT} and irrational numbers cannot be expressed as fractions of integer numbers (by definition, otherwise they would be rational numbers).

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
In the very first line of \cref{fig:pythonIntMathInConsoleArith}, we enter \pythonil{4 + 3}\pythonIdx{+} and hit \keys{\enter}.
3737
The result is displayed on the next line and, as expected, is \pythonil{7}.
38-
We then attempt to multiply the two integers \pythonil{7} and \pythonil{5} by typing \pythonil{7 * 5}\pythonIdx{*} and hitting \keys{\enter}.
38+
We then attempt to multiply the two integers \pythonil{7} and \pythonil{5} by typing \pythonil{7 * 5}\pythonIdx{*!multiplication} and hitting \keys{\enter}.
3939
The result is \pythonil{35}.
4040

4141
\python\ does not just support normal arithmetics as you have learned it in school, it also follows the operator precedence rules.
@@ -73,7 +73,7 @@
7373
\end{figure}%
7474

7575
As you will find in \cref{fig:pythonIntMathInConsolePower}, integers can also be raised to a power.
76-
For example, $2^7$~is expressed as \pythonil{2 ** 7}\pythonIdx{**} in \python\ (and yields~\pythonil{128}).
76+
For example, $2^7$~is expressed as \pythonil{2 ** 7}\pythonIdx{**!power} in \python\ (and yields~\pythonil{128}).
7777
\pythonil{7 ** 11}, i.e., $7^{11}$ gives us~1\decSep977\decSep326\decSep743 and shows as \pythonil{1977326743} in the output.
7878

7979
In most programming languages such as \pgls{Java} and \pgls{C}, the largest integer type available off the shelf is 64~bits wide.

text/main/controlFlow/functions/functions.tex

+142-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
%
77
\hsection{Defining and Calling Functions}%
88
%
9-
\pythonIdx{def}%
9+
\pythonIdx{def}\pythonIdx{function!def}%
1010
The syntax for defining our own functions in \python\ is as follows:%
1111
%
1212
\begin{pythonSyntax}
@@ -33,20 +33,25 @@
3333
my_function(argument_1, argument_2) # we can call the function like this
3434
\end{pythonSyntax}
3535
%
36-
\pythonIdx{function!def}A function in \python\ is created by using the \pythonilIdx{def} keyword, followed by the name of the function.%
36+
\pythonIdx{function!def}A function in \python\ is created by using the \pythonilIdx{def}\pythonIdx{function!def} keyword, followed by the name of the function.%
3737
%
3838
\bestPractice{functionNames}{Function names should be lower case, with underscores separating multiple words if need be~\cite{PEP8}.}%
3939
%
40-
\pythonil{function!parameters}Then follows an opening and a closing parenthesis, i.e., \pythonil{(...)}\pythonIdx{(\idxdots)}.
40+
\pythonil{function!parameters}\pythonIdx{function!parameter}Then follows an opening and a closing parenthesis, i.e., \pythonil{(...)}\pythonIdx{(\idxdots)}.
4141
A function can have parameters through which we can pass values to it.
4242
Inside the function, these parameters act like variables.
43-
The values of these variables can be passed in when we call (invoke, execute) the function.
43+
The values of these variables can be passed in when we call (invoke, execute) the function.%
44+
%
45+
\begin{definition}[Parameter]\pythonIdx{function!parameter}%
46+
A function \emph{parameter} is a variable defined inside the function that receives its value when the function is called.%
47+
\end{definition}%
48+
%
4449
Notice that, just like variables, all such parameters should be annotated with type hints~(see \cref{sec:variableTypesAndTypeHints}).
4550
Functions can return results~(like the \pythonilIdx{sqrt} function of the \pythonilIdx{math} module does) or return nothing~(like \pythonilIdx{print}).
4651
\pythonIdx{function!return value}If they return a result, the type of this result is specified via the type hint \pythonil{ -> result_type}\pythonIdx{->}.
4752
The function header ends with a colon~(\pythonilIdx{:}).%
4853
%
49-
\bestPractice{functionTypeHints}{%
54+
\bestPractice{functionTypeHints}{\pythonIdx{function!type hint}%
5055
All parameters and the return value of a function should be annotated with type hints. %
5156
From my perspective: \emph{A function without type hints is wrong.}%
5257
}%
@@ -66,6 +71,10 @@
6671
This follows the same pattern of function calls that we already used in many of our examples.%
6772
\end{sloppypar}%
6873
%
74+
\begin{definition}[Argument]\pythonIdx{function!argument}%
75+
An \emph{argument} is the actual value given for a function parameter when the function is called.%
76+
\end{definition}%
77+
%
6978
\pythonIdx{function!docstring}\pythonIdx{str!doc!function}%
7079
Between the header of a function and its body, we always need to place a so-called \pgls{docstring}, which is a multi-line string~(see \cref{sec:multiLineStrings}).
7180
This string consists of a title line shortly describing what the function does.
@@ -178,7 +187,7 @@
178187
Instead, it will print the \pythonil{gcd} nicely using \pythonilIdx{print} and an \pgls{fstring}.
179188
Since the \pythonilIdx{math} module also provides a function names \pythonilIdx{gcd} for computing, well, the greatest common divisor, we want to compare the result of our function with this one.%
180189
%
181-
\begin{sloppypar}%
190+
\begin{sloppypar}\pythonIdx{function!import}%
182191
Of course, we cannot have two functions named \pythonil{gcd} in the same context.
183192
So we import the function from the \pythonilIdx{math} module \emph{under a different name}:
184193
\pythonil{from math import gcd as math_gcd}\pythonIdx{as}\pythonIdx{import}\pythonIdx{from} makes the \pythonilIdx{gcd} function from the module \pythonilIdx{math} available under the name~\pythonil{math_gcd}.
@@ -193,6 +202,7 @@
193202
\endhsection%
194203
%
195204
\hsection{Functions in Modules}%
205+
\pythonIdx{module!import}\pythonIdx{function!import}\pythonIdx{function!module}%
196206
%
197207
\gitPython{\programmingWithPythonCodeRepo}{05_functions/my_math.py}{--args format}{functions:my_math}{%
198208
The module \pythonil{my_math}, which provides two mathematics functions, namely \pythonil{sqrt}, implementing the algorithm of Heron to compute the square root from \cref{lst:loops:while_loop_sqrt}, and \pythonil{factorial}, copied from \cref{lst:functions:def_factorial}.}%
@@ -262,6 +272,8 @@
262272
%
263273
\hsection{Unit Testing}%
264274
\label{sec:unitTesting}%
275+
\pythonIdx{function!unit test}%
276+
\pythonIdx{function!testing}%
265277
%
266278
Structuring our code into functions and modules has several advantages.
267279
We can reuse code and we can divide big application into smaller pieces, both of which make it easier to understand what our program is doing.
@@ -464,5 +476,129 @@
464476
}%
465477
\endhsection%
466478
%
479+
\hsection{Function Arguments: Default Values, Passing them by Name, and Constructing them}%
480+
%
481+
\gitPython{\programmingWithPythonCodeRepo}{05_functions/normal_pdf.py}{--args format}{functions:normal_pdf}{%
482+
Implementing the probability density function~(PDF) of the normal distribution as function with default argument values.\pythonIdx{pi}\pythonIdx{exp}\pythonIdx{sqrt}}%
483+
%
484+
\gitPythonAndOutput{\programmingWithPythonCodeRepo}{05_functions}{use_normal_pdf.py}{--args format}{functions:use_normal_pdf}{%
485+
Using the PDF of the normal distribution implemented in \cref{lst:functions:normal_pdf}.}%
486+
%
487+
After the brief introduction into unit testing, let us now come to a lighter topic: passing arguments to functions.%
488+
We have already seen examples for this.
489+
Our \pythonil{gcd} function from back in \cref{lst:functions:def_gcd} has two parameters \pythonil{a} and \pythonil{b} and we can invoke it by writing the values of these parameters in parentheses.
490+
\pythonil{gcd(12, 4)} will invoke \pythonil{gcd} and assign \pythonil{12} to \pythonil{a} and \pythonil{4} to \pythonil{b}.
491+
492+
We can also let parameters have so-called \emph{default values}.\pythonIdx{function!parameter!default value}\pythonIdx{function!argument!default}
493+
If a parameter has a default value, then we can either specify the value of the parameter when calling the function \emph{or} we can simply omit it, i.e., not assign a value to it.
494+
In the latter case, the parameter will then have the default value.
495+
From inside the function, this looks the same as if we passed in the default value.
496+
497+
As a simple example, let us implement the probability density function~(PDF) of the normal distribution.
498+
You may remember from high school math that this function, let's call it~$f$, defined as%
499+
%
500+
\begin{equation}%
501+
f(x, \mu, \sigma) = \frac{1}{\sqrt{2\numberPi\sigma^2}} e^{\frac{-(x-\mu)^2}{2\sigma^2}}%
502+
\label{eq:normalDistributionPdf}%
503+
\end{equation}%
504+
%
505+
\begin{figure}%
506+
\centering%
507+
\includegraphics[width=0.65\linewidth]{\currentDir/normalDistPdf}%
508+
\caption{A sketch of the probability density function of the normal distribution given in \cref{eq:normalDistributionPdf}.}%
509+
\label{fig:normalDistPdf}
510+
\end{figure}%
511+
%
512+
Here, $\mu$~is the arithmetic mean, i.e., the expected value, of the distribution and $\sigma$~is its standard deviation~(making $\sigma^2$~its variance).
513+
$x$~is the variable of this function.
514+
This function describes typical bell-shaped curve of the normal distribution as sketched in \cref{fig:normalDistPdf}.
515+
Implementing this function as a, well, function in \python\ is straightforward.
516+
\Cref{lst:functions:normal_pdf}\pythonIdx{pi}\pythonIdx{exp}\pythonIdx{sqrt} offers the function \pythonil{pdf} with three parameters: \pythonil{x}, \pythonil{mu}, and~\pythonil{sigma}, which represent~$x$, $\mu$, and~$\sigma$.%
517+
%
518+
\begin{sloppypar}\pythonIdx{function!call}%%
519+
Now, the two parameters~$\mu$ and~$\sigma$ of~$f$ (respectively~\pythonil{mu} and~\pythonil{sigma} of~\pythonil{pdf}) represent the general normal distribution.
520+
The \emph{standard} normal distribution has $\mu=0$ and $\sigma=1$, i.e., is centered around the mean~$0$ and has a standard deviation (and variance) of~$1$.
521+
We therefore define the \emph{default} values for \pythonil{mu} to be~\pythonil{0.0} and for \pythonil{sigma} to be~\pythonil{1.0}.
522+
This is done directly in the header of the function.
523+
Instead of writing \pythonil{x: float, mu: float, sigma: float}, we write \pythonil{x: float, mu: float = 0.0, sigma: float = 1.0}.%
524+
\end{sloppypar}%
525+
%
526+
In \cref{lst:functions:use_normal_pdf}, we import our new \pythonil{pdf} function and use it inside a program.
527+
When calling \pythonil{pdf}, we can omit the values of the parameters with default values, in which case they will take on their default values.
528+
For example, invoking \pythonil{pdf(0.0)} if equivalent to calling~\pythonil{pdf(0.0, 0.0, 1.0)}.
529+
We also can specify some of the parameters with default values while omitting others.
530+
For instance, the function call \pythonil{pdf(2.0, 3.0)} is the same as \pythonil{pdf(2.0, 3.0, 1.0)}.
531+
Obviously, we must always specify the first parameter~(\pythonil{x}), because it has no default value.%
532+
%
533+
\bestPractice{defaultValues}{%
534+
\pythonIdx{function!parameter!default value}\pythonIdx{function!argument!default}%
535+
Default parameter values must always be immutable.}%
536+
%
537+
The default value of a function must always be immutable.
538+
If you would pass in, e.g., a \pythonil{list}, then the function could modify the list and the next call to this function would then receive this modified list.
539+
Even worse, if the function was to return the list, it could be modified outside of the function.
540+
The behavior of such code could become arbitrarily hard to debug.
541+
542+
Back to business.\pythonIdx{function!parameter!by name}\pythonIdx{function!argument!by name}
543+
What would we do if we want to specify the value of the parameter \pythonil{sigma} of our function, but leave \pythonil{mu} at its default value?
544+
We can do this by passing in values by parameter name:
545+
\pythonil{pdf(-2.0, sigma=3.0)}~passes in \pythonil{-2.0} for~\pythonil{x} and \pythonil{3.0} for~\pythonil{sigma}.
546+
It does not specify any value for~\pythonil{mu}, leaving it at its default value, which renders the call equivalent to~\pythonil{pdf(-2.0, 0.0, 3.0)}.
547+
This passing in of arguments by specifying \pythonil{parameterName=value} also allows us to specify the arguments in arbitrary order.
548+
\pythonil{pdf(mu=8.0, x=0.0, sigma=1.5)} is an example of this.
549+
Don't do such things, though.%
550+
%
551+
\begin{sloppypar}%
552+
\Cref{lst:functions:use_normal_pdf} provides also another interesting way to call a function in \python.
553+
As we have established by now, the parameters of a function have names.
554+
If we write something like \pythonil{mu=8.0, x=0.0, sigma=1.5} to assign arguments, this looks very similar to the way we created dictionary constants back in \cref{sec:dictionaries} and \cref{lst:dicts:dicts_1}.
555+
Calling \pythonil{pdf(-2.0, sigma=3.0)} is equivalent to writing~\pythonil{pdf(x=-2.0, sigma=3.0)}.%
556+
\end{sloppypar}%
557+
%
558+
We can create a dictionary with the values \pythonil{\{"x": -2.0, "sigma": 3.0\}}.
559+
Let's call this dictionary~\pythonil{args_dict}.
560+
Can we now somehow pass in these values to~\pythonil{pdf}?
561+
We indeed can:
562+
We just have to write~\pythonil{pdf(**args_dict)}.\pythonIdx{**!function parameter}\pythonIdx{function!parameter!**}\pythonIdx{function!argument!**}\pythonIdx{function!argument!keyword}\pythonIdx{dict}
563+
Doing this will unpack the dictionary \pythonil{args_dict} and pass all the values under their assigned names in as arguments to their corresponding parameters.
564+
\pythonil{pdf(**args_dict)} is thus equivalent to~\pythonil{pdf(x=-2.0, sigma=3.0)}.
565+
Two things are to notice here:
566+
First, the double~\pythonil{*}\pythonIdx{*!function parameter}\pythonIdx{function!argument!*}\pythonIdx{function!parameter!*} (called %
567+
wildcard\pythonIdx{wildcard}\pythonIdx{function!argument!wildcard}\pythonIdx{function!parameter!wildcard}, %
568+
star\pythonIdx{star}\pythonIdx{function!argument!star}\pythonIdx{function!parameter!star}, or %
569+
asterisk\pythonIdx{asterisk}\pythonIdx{function!argument!asterisk}\pythonIdx{function!parameter!asterisk}) %
570+
\emph{before} the dictionary, i.e., the~\pythonil{**}\pythonIdx{**!function parameter} is telling \python\ to unpack the dictionary this way.
571+
Second, default argument values still apply here, i.e., \pythonil{mu} will have value~\pythonil{0.0} in this function call.
572+
573+
Similarly, maybe we do not care about the parameter names but want pass them in by position, as we have always done in the past.
574+
Then, we can construct a sequence, e.g., a \pythonilIdx{list} or \pythonilIdx{tuple} with the parameter values.
575+
Of course, \pythonilsIdx{list} and \pythonilsIdx{tuple} do not store key-value relationships, only values at positions.
576+
We could create a tuple~\pythonil{args_tuple} with the value~\pythonil{(-2.0, 7.0, 3.0)}.
577+
Then, invoking \pythonil{pdf(*args_tuple)} will basically fill in the three values in their into the parameters, i.e., will be equivalent to~\pythonil{pdf(-2.0, 7.0, 3.0)}.
578+
This time, only a single wildcard~\pythonil{*}\pythonIdx{*!function parameter}\pythonIdx{function!argument!*}\pythonIdx{function!parameter!*} is placed before~\pythonil{args_tuple}.
579+
We can also pass the parameters in by \inQuotes{unpacking} a list.
580+
In our example \cref{lst:functions:use_normal_pdf}, we create the list \pythonil{args_list = [2.0, 3.0]}.
581+
Calling~\pythonil{pdf(*args_list)} then is the same as writing~\pythonil{pdf(2.0, 3.0)}, which, in turn, is identical to~\pythonil{pdf(2.0, 3.0, 1.0)}.
582+
Again, parameters with default values do not need to be supplied.
583+
584+
At first glance, the use of all of the above is not entirely clear.
585+
What do we need default parameter values for?
586+
Well, in some cases, you may want to enable a user to \inQuotes{customize} your functions.
587+
A typical example is the \href{https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.plot.html}{\pythonilIdx{plot} method} of the \pythonilIdx{Axes} object provided by popular \matplotlib\ library.
588+
You will normally provide a sequence of x-\ and y\nobreakdash-coordinates to this function it will draw a line which goes through all the points specified this way.
589+
However, you can also optionally specify a color for the line, markers to be painted at the points, line dash style, a label, colors and sizes for the markers, a z\nobreakdash-order to be used if multiple lines are drawn, and so on, and so on.
590+
The use of default arguments allows the function call to be relatively simple in most cases, while still allowing the user to do more complex formatting if need be.
591+
592+
From this example, we can also directly extrapolate a use case for building the arguments of a function in a dictionary.
593+
Imagine that you write an own function that uses one of the plotting methods of \matplotlib.
594+
Let's say that your function does a plot call where it provides ten parameter values.
595+
However, you have one special case where you need to provide one more parameter, maybe a line dash style that you otherwise do not need to provide.
596+
Then, you could have some \pythonil{if} in your code that branches to do the ten-parameter-call in one case and the eleven-parameter-call in the other.
597+
This means that a rather complex function call appears twice in a very similar manner.
598+
If you instead construct the parameters in a dictionary and in the \pythonil{if} branch just add the eleventh parameter if need be, your code will become much simpler.%
599+
%
600+
\FloatBarrier%
601+
\endhsection%
602+
%
467603
\endhsection%
468604
%
Binary file not shown.

0 commit comments

Comments
 (0)