Skip to content

Latest commit

 

History

History
247 lines (196 loc) · 7.89 KB

ch_05_docs.md

File metadata and controls

247 lines (196 loc) · 7.89 KB

Documentation

"Documentation" can mean a few things in the Python community. At the simplest level, documentation can just be the docstrings included with your modules, classes, and functions. The next level up would probably be external user documentation files, probably written in Markdown or reStructuredText, using a utility such as Sphinx to convert the original files into other formats such as HTML or PDF. These documentation files are the ones anyone who wants to use your library would reference. Finally, if you want other contributors to have specific information about assisting with development, you can include external developer documentation files, also written in some markup language and converted to various formats with sphinx.

Docstrings

Even within the single format of docstrings, there are multiple ways to format the contents of the docstrings, such as Epydoc and Google's Python Style Guide. We'll be using the reStructuredText within our docstrings in order to allow Sphinx to automatically include them within the generated documentation files. We'll also be including some fairly superfluous docstrings, one for each multi-file module, module, class, and function, so that we can see exactly what Sphinx will do with them.

polygons/__init__.py becomes:

"""Provides utilities for calculating polygon areas.
"""

from . import polygon
from . import regularpolygon

__all__ = ['polygon', 'regularpolygon']

polygons/polygon/__init__.py:

"""Provides utilities for working with (irregular) polygons.
"""

from .polygon import Polygon

polygons/polygon/polygon.py:

class Polygon:
    """A class representing any polygon as a list of sides and a list of angles.
    """

    names = {
            3: 'triangle',
            4: 'quadrilateral',
            5: 'pentagon',
            6: 'hexagon',
            7: 'heptagon',
            8: 'octagon',
            9: 'nonagon',
            10: 'decagon',
    }

    def __init__(self, sides:int, lengths:list, angles:list):
        """Initialize the Polygon class.

           :param sides: The number of sides the polygon has
           :param lengths: A list of lengths for each side
           :param angles: A list of angles, angle i is between lengths i and i + 1
           :raises ValueError: if arguments are invalid for any reason
        """
        self.sides = sides
        self.lengths = lengths
        self.angles = angles
        
        self.confirm_attributes()

    @property
    def perimeter(self) -> float:
        """Return the perimiter of the polygon.
        """
        return sum(self.lengths)

    @property
    def name(self) -> str:
        """Return the name of the polygon.
        """
        return self.names[self.sides]

    def confirm_attributes(self):
        """Confirm attributes are within bounds restrictions.
        """
        if (not isinstance(self.sides, int)) and (not self.sides.is_integer()):
            raise ValueError('sides must be an integer')

        if type(self.lengths) is not list:
            raise ValueError('lengths must be a list')
        if len(self.lengths) != self.sides:
            raise ValueError('lengths count must equal number of sides')
        if not all(isinstance(length, (int, float)) for length in self.lengths):
            raise ValueError('lengths must be numeric')
        if not all(length > 0 for length in self.lengths):
            raise ValueError('lengths must be positive')

        if type(self.angles) is not list:
            raise ValueError('angles must be a list')
        if len(self.angles) != self.sides:
            raise ValueError('angles count must equal number of sides')
        if not all(isinstance(angle, (int, float)) for angle in self.angles):
            raise ValueError('angles must be numeric')
        if sum(self.angles) != ((self.sides - 2) * 180):
            raise ValueError('angles must sum correctly')
        if not all(angle > 0 for angle in self.angles):
            raise ValueError('angles must be positive')

polygons/regularpolygon/__init__.py:

"""Provides utilities for calculating regular polygon areas.
"""

from .regularpolygon import RegularPolygon
from .square import Square
from .equilateraltriangle import EquilateralTriangle

polygons/regularpolygon/regularpolygon.py:

import math
from ..polygon.polygon import Polygon

class RegularPolygon(Polygon):
    """A class representing any regular polygon.
    """

    def __init__(self, sides:int, length:float):
    """Initialize the RegularPolygon class.
    
       :param sides: The number of sides the regular polygon has
       :param length: The length of each side
       :raises ValueError: if sides or length are invalid values
    """
        self.length = length
        lengths = [length] * sides
        angles = [self.angle(sides)] * sides
        super().__init__(sides, lengths, angles)

    @property
    def perimeter(self) -> float:
        """Return the perimiter of the regular polygon.
        """
        return self.sides * self.length

    @property
    def apothem(self) -> float:
        """Return the apothem of the regular polygon.
        """
        tan = math.tan(math.pi / self.sides)
        return self.length / (2 * tan)

    @property
    def area(self) -> float:
        """Return the area of the regular polygon.
        """
        if self.sides == 4:
            return self.length ** 2
        else:
            return (self.apothem * self.perimeter) / 2

    @classmethod
    def angle(self, sides:int) -> float:
        """Return the calculated angle for a regular polygon with the given sides.

           :param sides: The number of sides of the regular polygon
           :raise ValueError: if sides < 3 or not an int
        """
        if sides < 3:
            raise ValueError('cannot calculate angles of polygon with less than 3 sides')
        elif type(sides) != int:
            raise ValueError('sides must be an int')
        angle_sum = 180 * (sides - 2)
        return angle_sum / sides

polygons/regularpolygon/square.py:

from .regularpolygon import RegularPolygon

class Square(RegularPolygon):
    """A class to represent a square.
    """

    def __init__(self, length:float):
        """Initialize the Square class.
        """
        super().__init__(4, length)

    @property
    def area(self):
        """Return the area of the square.
        """
        return self.length ** 2

    @property
    def name(self):
        """Override `Polygon.name` function.
        """
        return 'square'

polygons/regularpolygon/equilateraltriangle.py:

import math
from .regularpolygon import RegularPolygon

class EquilateralTriangle(RegularPolygon):
    """A class to represent an equilateral triangle.
    """

    def __init__(self, length:float):
        """Initialzie the EquilateralTriangle class.
        """
        super().__init__(3, length)

    @property
    def area(self) -> float:
        """Return the area of the equilateral triangle.
        """
        return (self.length ** 2) * math.sqrt(3) / 4

    @property
    def name(self) -> str:
        """Override Polygon.area function.
        """
        return 'equilateral triangle'

User Documentation

Developer Documentation

Next: Distribution