The purpose of this exercise is to test your new found knowledge of inheritance using the classical example of shapes. You have been given the base class (AbstractShape
) which has some common functions for certain derived shapes, such as a triangle and a rectangle.
from IPython.core.display import HTML
def css_styling():
sheet = '../css/custom.css'
styles = open(sheet, "r").read()
return HTML(styles)
css_styling()
Create a Rectangle class which also derives for AbstractShape, with methods
__init__
(self, base, height, center), which ONLY passes all arguments to the base class __init__
via
super().__init__(base, height, center)
get_vertices(self), which calculates and returns an array of vertices.
self.center
: numpy array with shape (4,2)get_area(self), which calculates and returns the area of self (use attributes, not inputs)
Points to note:
Repeat 1. above for a Triangle class
You have also been given a Cuboid
class which has inherited a plot method, but this will plot only the 2D square
See the end of this notebook for the magic methods extension
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
%matplotlib inline
class AbstractShape(object):
"""A class that shares common methods between rectangles and triangles:
Note that some methods raise an error - what we are doing here is
actually creating 'abstract methods' which helps achieve a consistent
API through all derived classes => This is Polymorphism!
See example 07-PredatorPrey for a more robust way of defining abstract
base classes
"""
def __init__(self, base, height, center):
# Add all args as attributes- there are quicker/better ways
# of doing this, but this is fine
self.base = base
self.height = height
self.center = center
# Call some methods: This will do nothing unless the methods are
# defined in the base classes!
self.vertices = self.get_vertices()
self.area = self.get_area()
def plot(self, ax):
# First point must be repeated for a closed plot
x = np.hstack([self.vertices[:, 0], self.vertices[0, 0]])
y = np.hstack([self.vertices[:, 1], self.vertices[0, 1]])
ax.plot(x, y, '-')
def get_vertices(self):
raise NotImplementedError('Base class method should not be called directly')
def get_area(self):
raise NotImplementedError('Base class method should not be called directly')
# Your classes here
# .................
class Cuboid(Rectangle):
def __init__(self, base, height, depth, center):
self.depth = depth
super().__init__(base, height, center)
def get_vertices(self):
base2d = super().get_vertices()
midplane = np.zeros([4,3])
midplane[:,:-1] = base2d
zshift = np.array([0, 0, self.depth/2.])
lower_plane = midplane - zshift
upper_plane = midplane + zshift
return np.vstack([lower_plane, upper_plane])
# Helper functions:
def init_figure():
fig = plt.figure()
ax = fig.add_subplot(111)
plt.axis('equal')
return fig, ax
def init_3dfigure():
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
return fig, ax
# Test code for the rectangle class:
fig, ax = init_figure()
for i in range(1,5):
rect = Rectangle(base=i, height=i, center=(0.,0.))
rect.plot(ax)
# Test code for the triangle class:
fig, ax = init_figure()
for height in range(1,5):
tri = Triangle(base=4, height=height, center=(0.,0.))
tri.plot(ax)
#Tests for the cuboid extension:
# This won't work: you need to redfine the plot method! see task 3
fig, ax = init_3dfigure()
cube = Cuboid(base=2, height=2, center=(0.,0.), depth=2)
cube.plot(ax)
Override the __str__
magic method in the AbstractShape
base class so that printing gives information about the area, base and height store in any shape instance
Override the 'less than' magic method in the AbstractShape
base class so that evaluating shape1 < shape2
evaluates whether the area of shape 1 is less than shape 2
# Test code for __str__
square = Rectangle(4, 4, center=(0.,0.))
print(square) # Did this do what you expected?
# Test code for iterator
bigsquare = Rectangle(8, 8, (0.,0.))
square < bigsquare
Code for Triangle.get_vertices
:
pts = np.ones([3, 2]) * self.center
pts[0,:] += np.array([-self.base/2., 0])
pts[1,:] += np.array([self.base/2., 0])
pts[2,:] += np.array([0, self.height])
return pts
Code for Rectangle.get_vertices
:
pts = np.ones([4, 2]) * self.center
xshift = self.base / 2.
yshift = self.height / 2.
pts[0,:] += np.array([-xshift, -yshift])
pts[1,:] += np.array([xshift, -yshift])
pts[2,:] += np.array([xshift, yshift])
pts[3,:] += np.array([-xshift, yshift])
return pts