Skip to content

Commit c138c26

Browse files
committed
more about class inheritance
1 parent 42e64a4 commit c138c26

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed
Binary file not shown.
Loading

text/main/classes/inheritance/inheritance.tex

+70-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
\hsection{Inheritance}%
22
\label{sec:inheritance}%
33
%
4+
\begin{figure}[tb]%
5+
\centering%
6+
\includegraphics[width=0.985\linewidth]{\currentDir/exampleInheritance}%
7+
\caption{An example for class inheritance. %
8+
The base class \pythonil{Shape} offers two abstract methods, \pythonil{area} and \pythonil{perimeter}. %
9+
The class \pythonil{Circle} inherits from \pythonil{Shape} and implements these methods. %
10+
It also has an attribute \pythonil{center}, which is an instance of~\pythonil{Point}, and an attribute~\pythonil{radius}. %
11+
The class \pythonil{Polygon} extends \pythonil{Shape} as well, and offers, amongst others, an \pythonilIdx{Iterable} of its~\pythonils{Point}. %
12+
It also uses our \pythonil{KahanSum} to implement the \pythonil{perimeter} method. %
13+
The class \pythonil{Polygon} is realized by the classes \pythonil{Triangle} and \pythonil{Rectangle}.}%
14+
\label{fig:exampleInheritance}%
15+
\end{figure}%
16+
%
417
\gitPython{\programmingWithPythonCodeRepo}{08_classes/shape.py}{--args format}{classes:shape}{%
518
A class for representing shapes in the two-dimensional Euclidean plane.}%
619
%
@@ -21,7 +34,8 @@
2134
%
2235
We already learned that classes are a proper tool four grouping data and operations on the data together into one semantic unit.
2336
Classes also offer the concept of inheritance, which basically means \inQuotes{specialization}.
24-
Imagine that we would wanted to represent all geometrical shapes in a two-dimensional plance.
37+
This concept is explored by a more elaborate example illustrated in \cref{fig:exampleInheritance}, which we will now discuss step-by-step.
38+
Imagine that we would wanted to represent all geometrical shapes in a two-dimensional plane.
2539
Each shape has an associated area as well as a perimeter.
2640
We could create a class\pythonIdx{class} \pythonil{Shape} that provides two methods, \pythonil{area} and \pythonil{perimeter}, returning the area in in area units and the perimeter length in length units, respectively.
2741

@@ -68,5 +82,60 @@
6882
It also holds that \pythonil{isinstance(cir, Shape)}\pythonIdx{isinstance}.
6983
Every instance of \pythonil{Circle} is also an instance of \pythonil{Shape}.
7084
Because \pythonil{Circle} is a special case of \pythonil{Shape}.
85+
86+
There are, of course, more shapes than just circles.
87+
Another very general class of shapes are polygons.
88+
Polygons are shapes enclosed by straight lines.
89+
This means that every polygon can be defined by its corner points.
90+
In \cref{lst:classes:polygon}, we define \pythonil{Polygon} as base class for such shapes.
91+
It is a special case of (and thus inherits from)~\pythonil{Shape}.
92+
93+
\pythonil{Polygon} extends the interface of \pythonil{Shape} by offering the method~\pythonil{points}.
94+
This method returns an \pythonilIdx{Iterable}\pythonIdx{typing.Iterable} of instances of~\pythonil{Point}, which are the corner points of the polygon.
95+
Our goal here is to provide a base class for different types of \pythonils{Polygon}.
96+
We do not actually want to implement a datastructure for arbitrary polygons here.
97+
So the method \pythonil{points} raises\pythonIdx{raise} an \pythonil{NotImplementedError} and thus must be implemented by the subclasses that we will develop later.
98+
99+
If we know the sequence corner points and also know that they are connected by straight lines, then we can easily compute the perimeter of such a shape.
100+
We can thus implement \pythonil{perimeter} as follows:
101+
We iterate over the instances of~\pythonil{Point} returned by~\pythonil{points()}.
102+
In a summation variable, we add up the distance between each point and its successor in the sequence.
103+
Finally, we add the distance of the last point to the first point.
104+
The distance can be computed with the \pythonil{distance} method offered by the \pythonil{Point} class.
105+
While \pythonil{Polygon} itself does not implement \pythonil{points}, its subclasses will.
106+
The method \pythonil{perimeter} will then \emph{automatically} use the actual implementation of the subclass.
107+
We therefore can implement \pythonil{perimeter} here, even if \pythonil{points} is not yet implemented.
108+
If we create a subclass that does implement \pythonil{points} and call \pythonil{perimeter} upon an instance of this subclass, it will work.
109+
110+
The summation could be easily done with a normal variable of type~\pythonil{float}.
111+
However, since we implemented the second-order \citeauthor{K1965PFRORTE}-\citeauthor{B1968NSIMA}-\citeauthor{N1974REVZSES} summation algorithm in~\cref{lst:classes:kahan_sum}, we instead use that one.
112+
It should give us a very accurate result.
113+
(Notice that we could not easily use~\pythonilIdx{fsum} here without first storing all distances in a list, because we need to add up over the sequence of points and also the distance from the last to the first point.
114+
So our \pythonil{KahanSum} does have indeed some advantages.)
115+
For convenience sake, we also add a method~\pythonil{print} to \pythonil{Polygon}, which just prints the sequence of points.
116+
117+
Rectangles and triangles are special cases of polygons.
118+
We now implement them as classes \pythonil{Rectangle} and \pythonil{Triangle} in \cref{lst:classes:rectangle,lst:classes:triangle}, respectively, which both are subclasses of~\pythonil{Polygon}.
119+
120+
A rectangle can be defined using its bottom-left and top-right corner point.
121+
In the constructor \pythonilIdx{\_\_init\_\_} of the class \pythonil{Rectangle}, we pass in two points~\pythonil{p1} and~\pythonil{p2}.
122+
We raise\pythonIdx{raise} a \pythonilIdx{ValueError} if these would form an empty rectangle.
123+
Otherwise, we store the minimum x\nobreakdashes-~and y\nobreakdashes-coordinate in the bottom-left point attribute~\pythonil{p1} and the maximum x\nobreakdashes-~and y\nobreakdashes-coordinate in the attribute~\pythonil{p2}, marking the top-right corner of our rectangle.
124+
125+
We implement the method \pythonil{points} to return all four corners of the rectangle.
126+
We only needed to store two of them, but we here need to return all four to comply with the definition of the method as given in class~\pythonil{Polygon}.
127+
128+
The area of the rectangle is easily computed.
129+
While we already inherit a perfectly fine method \pythonil{perimeter} computing the perimeter of our rectangle from \pythonil{Polygon} we still override it.
130+
This makes sense because we can compute the perimeter faster and more exactly by simply returning twice the sum of the length of the horizontal and vertical side length of our rectangle.
131+
The inherited \pythonil{perimeter} method would instead iterate over all four points and compute Euclidean distances using~\pythonil{sqrt}, which is both slower and less accurate (especially if our coordinates would be~\pythonils{int}).
132+
133+
The class \pythonil{Triangle} is implemented in very much the same fashion.
134+
This time, we need to store all three corner points (and also return all of them in the \pythonil{points}~method implementation).
135+
There also is no better way for computing the perimeter than what \pythonil{Polygon} already provides, so we this time do not override~\pythonil{perimeter}.
136+
The area computation is implemented using the formula~$A=x_1(y2-y3)+x_2(y_3-y_1)+x_3(y_1-y_2)$ you probably remember from high school maths.
137+
138+
Notice that both subclasses of \pythonil{Polygon} offer some \pglspl{doctest} in the \pglspl{docstring}.
139+
While I needed to keep the \pglspl{docstring} short to be able to fit the listings on pages, these \pglspl{doctest} still are instructive for the user.%
71140
%
72141
\endhsection%

0 commit comments

Comments
 (0)