Skip to content

Commit d3ba042

Browse files
committed
finished implementing arithmetic dunder
1 parent 73ce171 commit d3ba042

File tree

1 file changed

+74
-3
lines changed

1 file changed

+74
-3
lines changed

text/main/classes/dunder/dunder.tex

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -424,16 +424,87 @@
424424
Anyway, with the string conversion out of the way, we can begin to implement mathematical operators.
425425

426426
\gitPython{\programmingWithPythonCodeRepo}{09_dunder/fraction.py}{--args format --labels part_3}{dunder:fraction:part_3}{%
427-
Part~3 of the \pythonil{Fraction} class: Addition (via \pythonilIdx{\_\_add\_\_}) and subtraction (via \pythonilIdx{\_\_sub\_\_}).}%
427+
Part~3 of the \pythonil{Fraction} class: Addition (via \pythonilIdx{\_\_add\_\_}\pythonIdx{dunder!\_\_add\_\_}) and subtraction (via \pythonilIdx{\_\_sub\_\_}\pythonIdx{dunder!\_\_sub\_\_}).}%
428428
%
429429
\gitPython{\programmingWithPythonCodeRepo}{09_dunder/fraction.py}{--args format --labels part_4}{dunder:fraction:part_4}{%
430-
Part~4 of the \pythonil{Fraction} class: Multiplication (via \pythonilIdx{\_\_mul\_\_}) and division (via \pythonilIdx{\_\_truediv\_\_})}%
430+
Part~4 of the \pythonil{Fraction} class: Multiplication (via \pythonilIdx{\_\_mul\_\_}\pythonIdx{dunder!\_\_mul\_\_}), division (via \pythonilIdx{\_\_truediv\_\_}\pythonIdx{dunder!\_\_truediv\_\_}), and computing the absolute value (via \pythonilIdx{\_\_abs\_\_}\pythonIdx{dunder!\_\_abs\_\_}).}%
431431
%
432+
\afterpage{\clearpage}
433+
434+
In \cref{lst:dunder:fraction:part_3}, we want to enable our \pythonil{Fraction} class to be used with the \pythonil{+} and \pythonil{-} operators.
435+
In \python, doing something like \pythonil{x + y} will invoke \pythonil{x.\_\_add\_\_(y)}, if the class of~\pythonil{x} defines the \pythonilIdx{\_\_add\_\_}\pythonIdx{dunder!\_\_add\_\_} method.
436+
From primary school, we remember that $\frac{a}{b}+{c}{d} = \frac{a*d+c*b}{b*d}$.
437+
Therefore, if \pythonil{other} is also an instance\pythonIdx{isinstance} of \pythonil{Fraction}, \pythonil{\_\_add\_\_(other)} computes the result like that and creates a new \pythonil{Fraction}.
438+
Notice that the initializer of that new fraction will automatically normalize the fraction by using~\pythonilIdx{gcd}.
439+
If \pythonil{other} is not an instance of \pythonil{Fraction}, we return \pythonilIdx{NotImplemented}, because this would enable \python\ to look for other routes to perform addition with our objects.\footnote{%
440+
\python\ would then look whether \pythonil{other} provides an \pythonilIdx{\_\_radd\_\_}\pythonIdx{dunder!\_\_radd\_\_} method that does not return~\pythonilIdx{NotImplemented} {\dots} but we will not implement all possible arithmetic dunder methods here so we skip this one.%
441+
}%
442+
The behavior of this method is again be tested with \pglspl{doctest}.
443+
These check that $\frac{1}{3} + \frac{1}{2}$ actually yields~$\frac{5}{6}$ and that $\frac{1}{2}+\frac{1}{2}$ really returns~$\frac{1}{1}$.
444+
They also check correct normalization by trying~$\frac{21}{-12}+\frac{-33}{42}=\frac{882+396}{-504}=\frac{1278}{-504}=\frac{18*1278}{18*-28}=\frac{-71}{28}$.
445+
446+
After confirming that these tests succeed, we continue by implementing the \pythonilIdx{\_\_sub\_\_}\pythonIdx{dunder!\_\_sub\_\_} method in exactly the same way.
447+
This enables subtraction by using~\pythonilIdx{-}, because \pythonil{x - y} will invoke \pythonil{x.\_\_sub\_\_(y)}, if the class of~\pythonil{x} defines the \pythonilIdx{\_\_sub\_\_}\pythonIdx{dunder!\_\_sub\_\_} method.
448+
Clearly, $\frac{a}{b}-{c}{d} = \frac{a*d-c*b}{b*d}$.
449+
As \pglspl{doctest}, the same three cases as used for \pythonil{\_\_add\_\_} will do.
450+
451+
In \cref{lst:dunder:fraction:part_4}, we now focus on multiplication and division.
452+
The \pythonil{*}~operation will utilize a \pythonilIdx{\_\_mul\_\_}\pythonIdx{dunder!\_\_mul\_\_}, if implemented, and that \pythonil{/}~operation uses \pythonilIdx{\_\_truediv\_\_}\pythonIdx{dunder!\_\_truediv\_\_}.
453+
Multiplying the fractions~$\frac{a}{b}$ and~$\frac{c}{d}$ yields~$\frac{a*c}{b*d}$.
454+
Dividing~$\frac{a}{b}$ by~$\frac{c}{d}$ yields~$\frac{a*d}{b*c}$.
455+
The dunder methods can be implemented according to the same schematic as before.
456+
We test multiplication by confirming that $\frac{6}{19}*\frac{3}{-7}=\frac{6 * 3}{19*-7}=\frac{18}{-133}=\frac{-18}{133}$.
457+
The division is tested by computing whether $\frac{6}{19}*\frac{3}{-7}=\frac{6 * -7}{19*3}=\frac{-42}{57}=\frac{3*-14}{3*19}$ indeed gives us~$\frac{-14}{19}$.
458+
459+
Now we also implement support for the \pythonilIdx{abs} function.
460+
\pythonilIdx{abs} returns the absolute value of a number.
461+
Therefore, $\pythonil{abs(5)}=\pythonil{abs(-5)}=\pythonil{5}$.
462+
If present, \pythonil{abs(x)} will invoke~\pythonil{x.\_\_abs\_\_()}\pythonIdx{\_\_abs\_\_}\pythonIdx{dunder!\_\_abs\_\_}.
463+
We can implement this method as follows:
464+
If our fraction is positive, then it can be returned as-is.
465+
Otherwise, we return a new, positive variant of our fraction by simply flipping the sign of it.
466+
432467
\gitPython{\programmingWithPythonCodeRepo}{09_dunder/fraction.py}{--args format --labels part_5}{dunder:fraction:part_5}{%
433-
Part~5 of the \pythonil{Fraction} class: All order-related dunder methods\pythonIdx{\_\_eq\_\_}\pythonIdx{\_\_ne\_\_}\pythonIdx{\_\_lt\_\_}\pythonIdx{\_\_le\_\_}\pythonIdx{\_\_gt\_\_}\pythonIdx{\_\_ge\_\_}.}%
468+
Part~5 of the \pythonil{Fraction} class: All order-related dunder methods\pythonIdx{\_\_eq\_\_}\pythonIdx{\_\_ne\_\_}\pythonIdx{\_\_lt\_\_}\pythonIdx{\_\_le\_\_}\pythonIdx{\_\_gt\_\_}\pythonIdx{\_\_ge\_\_}\pythonIdx{dunder!\_\_eq\_\_}\pythonIdx{dunder!\_\_ne\_\_}\pythonIdx{dunder!\_\_lt\_\_}\pythonIdx{dunder!\_\_le\_\_}\pythonIdx{dunder!\_\_gt\_\_}\pythonIdx{dunder!\_\_ge\_\_}.}%
434469
%
435470
\gitOutputTool{\programmingWithPythonCodeRepo}{.}{scripts/pytest_doctest.sh 09_dunder fraction.py}{dunder:fraction:doctest}{%
436471
The output of \pytest\ executing the \pglspl{doctest} for our \pythonil{Fraction} class.}%
472+
%
473+
Finally, in \cref{lst:dunder:fraction:part_5} we implement the six rich comparison dunder methods given in~\cite{PEP207}:%
474+
%
475+
\begin{itemize}%
476+
\item \pythonilIdx{\_\_eq\_\_}\pythonIdx{dunder!\_\_eq\_\_} implements the functionality of~\pythonilIdx{==}, as we already discussed before.%
477+
%
478+
\item \pythonilIdx{\_\_ne\_\_}\pythonIdx{dunder!\_\_ne\_\_} implements the functionality of~\pythonilIdx{!=}.%
479+
%
480+
\item \pythonilIdx{\_\_lt\_\_}\pythonIdx{dunder!\_\_lt\_\_} implements the functionality of~\pythonilIdx{<}.%
481+
%
482+
\item \pythonilIdx{\_\_le\_\_}\pythonIdx{dunder!\_\_le\_\_} implements the functionality of~\pythonilIdx{<=}.%
483+
%
484+
\item \pythonilIdx{\_\_gt\_\_}\pythonIdx{dunder!\_\_gt\_\_} implements the functionality of~\pythonilIdx{>}.%
485+
%
486+
\item \pythonilIdx{\_\_ge\_\_}\pythonIdx{dunder!\_\_ge\_\_} implements the functionality of~\pythonilIdx{>=}.%
487+
\end{itemize}%
488+
%
489+
Implementing equality and inequality is rather easy, since our fractions are all normalized.
490+
For two fractions~\pythonil{x} and~\pythonil{y}, it holds only that~\pythonil{x == y} if \pythonil{x.a == y.a} and \pythonil{x.b == y.b}.
491+
\pythonilIdx{\_\_eq\_\_}\pythonIdx{dunder!\_\_eq\_\_} is thus quickly implemented.
492+
\pythonilIdx{\_\_ne\_\_}\pythonIdx{dunder!\_\_ne\_\_} is its complement for the \pythonilIdx{!=}~operator.
493+
\pythonil{x != y} is \pythonil{True} if either \pythonil{x.a != y.a} or \pythonil{x.b != y.b}.
494+
495+
The other four comparison methods can be implemented by remembering how we used the common \pgls{denominator} for addition and subtraction.
496+
We did addition like this:~$\frac{a}{b}+\frac{c}{d}=\frac{a*d}{b*d}+\frac{c*b}{b*d}=\frac{a*d+c*b}{b*d}$.
497+
Looking at this again, we realize that $\frac{a}{b}<\frac{c}{d}$ is the same as~$\frac{a*d}{b*d}<\frac{c*b}{b*d}$, which must be the same as~$a*d<c*b$.
498+
Thus, $\frac{a}{b}\leq\frac{c}{d}$ is the same as~$a*d\leq c*b$.
499+
The greater and greater-or-equal operations can be defined the other way around.
500+
501+
All six comparison operations are defined accordingly in \cref{lst:dunder:fraction:part_5}.
502+
This time, I omitted \pglspl{doctest} for the sake of space.
503+
You should never do that, because in program code, you \emph{do} have space.
504+
Your code does not need to fit on book pages.
505+
506+
Anyway, in \cref{exec:dunder:fraction:doctest} we present the output of \pytest\ running the \pglspl{doctest} of all the methods we implemented.
507+
All of them succeed.%
437508
\FloatBarrier%
438509
\endhsection%
439510
%

0 commit comments

Comments
 (0)