Inheritance Example: 2D Geometry (Solutions)

The classes below represent simple 2D shapes.

  1. Simplify the code using inheritance. You can either cut-and-paste the code and modify it in the cell below, or modify the original code.
from math import sqrt

class Polygon(object):
    """ Represents a polygon in 2D """
    def __init__(self, vertices):
        """ Initialize a polygon from a list of vertices, where each
            vertex is represented as an (x, y) tuple.
            
            vertices: the vertices of the polygone (note: vertices
                      are assumed to be in counterclockwise order) """
        self.vertices = vertices
    
    def get_area(self):
        """
        >>> Polygon([(0, 0), (200, 0), (200, 300), (0, 300)]).get_area()
        60000.0
        """
        # Using formula from http://mathworld.wolfram.com/PolygonArea.html
        area = 0.0
        for i, v_i in enumerate(self.vertices):
            v_i_plus_1 = self.vertices[(i+1) % len(self.vertices)]
            area += 0.5*(v_i[0]*v_i_plus_1[1] - v_i[1]*v_i_plus_1[0])
        return area

    def get_side_lengths(self):
        """ Returns a list of all the side lengths in the Polygon
        
        >>> Polygon([(0, 0), (200, 0), (200, 300), (0, 300)]).get_side_lengths()
        [200.0, 300.0, 200.0, 300.0]
        """
        side_lengths = []
        for i, v_i in enumerate(self.vertices):
            v_i_plus_1 = self.vertices[(i+1) % len(self.vertices)]
            # calculate side_length using the Pythagorean theorem
            side_length = sqrt((v_i[0] - v_i_plus_1[0])**2 + (v_i[1] - v_i_plus_1[1])**2)
            side_lengths.append(side_length)
        return side_lengths
    
class Rectangle(Polygon):
    """ Represents a rectangle in 2D
    
        >>> Rectangle(3, 5, 100, 20).get_area()
        2000.0
    """

    def __init__(self, x1, y1, width, height):
        """ Initialize a rectangle from the upper left corner vertex and
            its width and height.
            
            x1: x-coordinate of the upper left corner of the rectangle
            y1: y-coordinate of the upper left corner of the rectangle
            width: width of the rectangle
            height: height of the rectangle
        """
        super().__init__([(x1, y1), (x1 + width, y1), (x1 + width, y1 + height), (x1, y1 + height)])

class Triangle(Polygon):
    """ Represents a triangle in 2D

        >>> Triangle(0.0, 0.0, 10.0, 0.0, 0.0, 5.0).get_area()
        25.0
    """

    def __init__(self, x1, y1, x2, y2, x3, y3):
        """ Initialize a triangle from its three vertices.  The vertices
            are assumed to be in counterclockwise order.
            
            x1: x-coordinate of the first vertex of the triangle
            y1: y-coordinate of the first vertex of the triangle
            x2: x-coordinate of the second vertex of the triangle
            y2: y-coordinate of the second vertex of the triangle
            x3: x-coordinate of the third vertex of the triangle
            y3: y-coordinate of the third vertex of the triangle
        """
        super().__init__([(x1, y1), (x2, y2), (x3, y3)])

class Square(Rectangle):
    """ Represents a square in 2D

        >>> Square(1.0, 3.0, 50.0).get_area()
        2500.0
    """

    def __init__(self, x1, y1, side_length):
        """ Initialize a square from the upper left corner vertex and
            its side length.
            
            x1: x-coordinate of the upper left corner of the rectangle
            y1: y-coordinate of the upper left corner of the rectangle
            side_length: side length of the square
        """
        super().__init__(x1, y1, side_length, side_length)


import doctest
doctest.testmod()
TestResults(failed=0, attempted=5)
  1. Create a right triangle class (make sure to use inheritance!)
  2. Add a function called `get_hypotenuse_length` to your right triangle class.
class RightTriangle(Triangle):
    """
    Represents a right triangle in 2D
    
    Note: the __init__ method takes three vertices (just like for the normal Triangle class),
          however, in this class we check to make sure the vertices actually represent
          a right triangle.

    >>> RightTriangle(0, 0, 3, 0, 0, 4).get_hypotenuse_length()
    5.0
    """
    def __init__(self, x1, y1, x2, y2, x3, y3):
        super().__init__(x1, y1, x2, y2, x3, y3)
        l = self.get_side_lengths()

        # make sure it's a right triangle by seeing if the Pythagorean theorem is satisfied
        if (l[0]**2 != l[1]**2 + l[2]**2 and
            l[1]**2 != l[0]**2 + l[2]**2 and
            l[2]**2 != l[0]**2 + l[1]**2):
            raise ValueError('The specified triangle is not a right triangle')
    
    def get_hypotenuse_length(self):
        return max(self.get_side_lengths())

doctest.testmod()
TestResults(failed=0, attempted=6)