`| A callable (e.g. a function or a method) that takes in arguments of specified types and returns the speficied type | A function that accepts a string and returns an integer: `Callable[[str], int]`
Method that takes in a string and an integer and returns `None`: `Callable[[str, int], None]`
Function that accepts arbitrary arguments and returns a boolean: `Callable[..., bool]`|"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's take, for example, a function that takes in:\n",
+ "\n",
+ "- a dictionary of student grades, which maps names (strings) to grades (list of floats)\n",
+ "- a custom function for computing statistics over a list of numbers\n",
+ "- a list of names of students for whom we want to compute statistics, whose default is all students\n",
+ "\n",
+ "and returns a list of tuples, with each tuple containing a student's name and their stat.\n",
+ "\n",
+ "```python\n",
+ "from typing import Dict, Callable, Optional, List, Tuple, Any\n",
+ "\n",
+ "def compute_student_stats(grade_book: Dict[str, List[float]],\n",
+ " stat_function: Callable[[List[float]], Any],\n",
+ " student_list: Optional[List[str]] = None) -> List[Tuple[str, Any]]:\n",
+ " \"\"\" Computes custom statistics over student's grades.\n",
+ " \n",
+ " Parameters\n",
+ " ----------\n",
+ " grade_book : Dict[str, List[float]]\n",
+ " The dictionary (name -> grades) of all of the students' grades. \n",
+ "\n",
+ " stat_function: Callable[[List[float]], Any]\n",
+ " The function used to compute statistics over each student's grades.\n",
+ " \n",
+ " student_list : Optional[List[str]]\n",
+ " A list of names of the students for whom statistics will be computed. \n",
+ " By default, statistics will be computed for all students in the gradebook.\n",
+ " \n",
+ " Returns\n",
+ " -------\n",
+ " List[Tuple[str, Any]]\n",
+ " The name-stats tuple pair for each specified student.\n",
+ " \"\"\"\n",
+ " if student_list is None: # default to all-students\n",
+ " student_list = sorted(grade_book) # iterates over the dictionary's keys\n",
+ " return [(name, stat_function(grade_book[name])) for name in student_list]\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "(Note that we have also included a detailed NumPy-style docstring; we will discuss this documentation style in detail in the following subsection).\n",
+ "\n",
+ "There are a few details to take note of here. First, see that we need not make any assumptions about what type of object is returned by `stat_function`. It might return the mean-value as a single floating point number, or perhaps a tuple of the mean, median, and mode, etc. Thus we document its output type as `Any`; this is reflected in our function's return type as well: `List[Tuple[str, Any]]`. As we will discuss shortly, it is prudent to be discerning but general in one's type-hints. Next, note that `Optional[List[str]]` is used to indicate that the default-value of `student_list` is `None`, but that we can otherwise pass it a list-of-strings. This is cleaner to read than the equivalent hint `Union[None, List[str]]` and it indicates that `None` is indeed the default-value. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the case that you have [defined your own type (akaclass)](http://www.pythonlikeyoumeanit.com/module_4.html), you can simply provide the resulting class object in an annotation. This type-hints that a variable is expected to be an *instance* of that type. If, instead, you have the rare case of hinting that the class object itself (not an instance of the class) is expected, you can use `typing.Type` to indicate this.\n",
+ "\n",
+ "Let's write a silly example to demonstrate these points. We will define our own `Dog` class, and will write a function that expects the `Dog` class-object itself (type-hinted as `Type[Dog]`). The function will create several *instances* of the `Dog` class (type-hinted simply as `Dog`), and will return them in a list."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "```python\n",
+ "# type-hinting a custom class\n",
+ "from typing import List, Type\n",
+ "class Dog:\n",
+ " def __init__(self, name):\n",
+ " self.name = name\n",
+ "\n",
+ "# cls is expected to be the class-object, `Dog`, itself\n",
+ "# This function returns a list of instances of the `Dog` type \n",
+ "def list_famous_dogs(cls: Type[Dog]) -> List[Dog]:\n",
+ " return [cls(name) for name in [\"Lassie\", \"Shadow\", \"Air Bud\"]]\n",
+ "```\n",
+ "\n",
+ "You can also create a [type alias](https://docs.python.org/3/library/typing.html#type-aliases), which you can define in your code and use in your annotations. For example, if you will regularly be passing around a tuple containing five `Dog`-instances, you can define an alias to help make your type-hints more succinct:\n",
+ "\n",
+ "```python\n",
+ "# creating an alias for a type\n",
+ "Pack = Tuple[Dog, Dog, Dog, Dog, Dog]\n",
+ "\n",
+ "def find_alpha(dogs: Pack) -> Dog:\n",
+ " ...\n",
+ "```\n",
+ "\n",
+ "Object types defined in 3rd-party libraries, e.g. NumPy's [ndarray](https://www.pythonlikeyoumeanit.com/Module3_IntroducingNumpy/IntroducingTheNDarray.html) behave no differently from our own custom custom classes; simply provide the [class object](https://www.pythonlikeyoumeanit.com/Module4_OOP/ClassDefinition.html#Defining-a-New-Class-of-Object) in the type-hint annotation:\n",
+ "\n",
+ "```python\n",
+ "# type-hinting an nd-array from numpy\n",
+ "import numpy as np\n",
+ "\n",
+ "def custom_dot_product(x: np.ndarray, y: np.ndarray) -> float:\n",
+ " return float(np.sum(x * y))\n",
+ "```\n",
+ "\n",
+ "\n",
+ "Eventually, popular 3rd party libraries like NumPy will contribute their own typing modules so that you can provide higher-fidelity hints that indicate things like data-type and array-shape. NumPy developers [are currently working on this](https://github.com/numpy/numpy/issues/7370)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "**Takeaway:**\n",
+ "\n",
+ "Python's `typing` module contains objects that are used to create descriptive type-hints. For example, whereas `list` can only be used to type-hint a list, `typing.List[str]` describes a list-of-strings. These can also be used to create concise aliases for convoluted type-hints that will be used frequently throughout your code. \n",
+ "\n",
+ "In general, a [class object](https://www.pythonlikeyoumeanit.com/Module4_OOP/ClassDefinition.html#Defining-a-New-Class-of-Object) can be used for type-hinting, and it indicates that a variable should be an *instance* of that class. For example, `numpy.ndarray` hints that a variable should be passed an instance of the nd-array class.\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Writing Good Type-Hints (quack quack)\n",
+ "\n",
+ "You should strive to write type-hints that pass [the duck test](https://en.wikipedia.org/wiki/Duck_test): if your function is expecting a duck then hint for something that walks like a duck, quacks like a duck, etc. This will help you avoid writing type-hints that are overly narrow, and which are ultimately non-Pythonic in their strictness.\n",
+ "\n",
+ "To be more concrete, let's revisit our `count_vowels` function:\n",
+ "\n",
+ "```python\n",
+ "def count_vowels(x: str, include_y: bool = False) -> int:\n",
+ " \"\"\" Returns the number of vowels contained in `in_string`\"\"\"\n",
+ " vowels = set(\"aeiouAEIOU\")\n",
+ " if include_y:\n",
+ " vowels.update(\"yY\")\n",
+ " return sum(1 for char in x if char in vowels)\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Look carefully at how `x` is used in this function. We simply iterate over `x` in a for-loop - there is nothing string-specific about this. We could pass a string, a tuple, a list, or anything that supports iteration to this function, and it will happily count the vowels in `x`:\n",
+ "\n",
+ "```python\n",
+ "# `count_vowels` can operate on any iterable of strings\n",
+ ">>> count_vowels(\"apple\")\n",
+ "2\n",
+ "\n",
+ ">>> count_vowels(['a', 'p', 'p', 'l', 'e'])\n",
+ "2\n",
+ "\n",
+ ">>> count_vowels(('a', 'p', 'p', 'l', 'e'))\n",
+ "2\n",
+ "\n",
+ ">>> count_vowels({'a', 'p', 'p', 'l', 'e'})\n",
+ "2\n",
+ "```\n",
+ "\n",
+ "It is over-restrictive and un-Pythonic to type-hint `x` as `str`. Let's make our type-hint more accommodating."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `typing` module provides a so-called [abstract base class](https://docs.python.org/3/glossary.html#term-abstract-base-class), `Iterable`, which is a generic type for any class that supports iteration. Thus we can improve our type-hint by making it more general. This encapsulates all of the above use-cases that we demonstrated.\n",
+ "\n",
+ "```python\n",
+ "from typing import Iterable\n",
+ "\n",
+ "def count_vowels(x: Iterable[str], include_y: bool = False) -> int:\n",
+ " \"\"\" Returns the number of vowels contained in `in_string`\"\"\"\n",
+ " vowels = set(\"aeiouAEIOU\")\n",
+ " if include_y:\n",
+ " vowels.update(\"yY\")\n",
+ " return sum(1 for char in x if char in vowels)\n",
+ "```\n",
+ "\n",
+ "(To be completely general, we could have hinted `Iterable[Hashable]`, as we are relying on the entries of `x` to be hashable to check for their membership in the [set](http://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/DataStructures_III_Sets_and_More.html#The-%E2%80%9CSet%E2%80%9D-Data-Structure) of vowels. It is up to you to determine how abstract you want your ducks to be.)\n",
+ "\n",
+ "It is is important to review the [abstract base classes (abc's) for Python's collections](https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes) (yes, I am encouraging you to learn your abc's). This will help you classify categories of types generally, based on their [special methods](http://www.pythonlikeyoumeanit.com/Module4_OOP/Special_Methods.html). Two of the most common abc's are `Iterable`: any class that supports the iteration protocol, and `Sequence`: any collection that has a length (via `__len__`) and supports the get-item syntax (via `__getitem__`)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "**Reading Comprehension: Type Hinting**\n",
+ "\n",
+ "Read through the following function and annotate its signature with type-hints. Try to make your type-hints sufficiently general here.\n",
+ "\n",
+ "```python\n",
+ "def get_first_and_last(x):\n",
+ " \"\"\" Returns the first and last elements of `x`. `x` is \n",
+ " assumed to be non-empty\n",
+ " \"\"\" \n",
+ " return (x[0], x[-1])\n",
+ "```\n",
+ "\n",
+ "Here are some examples of this function in action. Be sure that your type-hint captures all of this diversity.\n",
+ "\n",
+ "```python\n",
+ ">>> get_first_and_last(\"hello\")\n",
+ "('h', 'o')\n",
+ "\n",
+ ">>> get_first_and_last([0, 1, 2, 3])\n",
+ "(0, 3)\n",
+ "\n",
+ ">>> get_first_and_last((True, False))\n",
+ "(True, False)\n",
+ "```\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "**Takeaway:**\n",
+ "\n",
+ "It is un-Pythonic to be overly strict when type hinting. Python handles typing flexibly - it uses [duck typing](https://en.wikipedia.org/wiki/Duck_typing) - and our type hints should follow suit. Rely on abstract base classes when possible to provide the appropriate guidelines for a particular variable type. \n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Documentation Styles\n",
+ "\n",
+ "[Davis King](https://twitter.com/nulhom?lang=en), a prolific and talented open source developer, is the creator of the [dlib C++ library](http://dlib.net/). Among the library's major features, like machine learning algorithms, Davis lists its *documentation* as the first and foremost feature. In fact, he says about dlib:\n",
+ "\n",
+ "> *I consider the documentation to be the most important part of the library.* So if you find anything that isn't documented, isn't clear, or has out of date documentation, tell me and I will fix it.\n",
+ "\n",
+ "There is great wisdom in placing such a high value on documentation. In this section, we will learn about two popular docstring-style specifications for Python: the NumPy specification and the Google specification. Both of these are rich extensions of the rudimentary docstring conventions that are proposed in [PEP 257](https://www.python.org/dev/peps/pep-0257/), and, critically, they both place an emphasis on documenting the variable types. \n",
+ "\n",
+ "PLYMI uses Numpy-style docstrings throughout most of the text (except for when we are trying to keep the functions brief). This is ultimately just a choice of style/aesthetics. Ultimately, the critical takeaway here is to **pick a documentation style, learn it, and stick to it faithfully**. Once again, it is hard to overstate how important it is to anchor your code with clear and consistent documentation. It will aid you in your code-writing process, it will enable users to adopt and perhaps contribute to your code, and it will ensure longevity for your hard work."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The NumPy Documentation Style\n",
+ "\n",
+ "The NumPy documentation style is specified in full [here](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard). It is strongly recommended that you read through it in full. There are details in the specification that we will pass over here for the sake of simplicity and to avoid rewriting their specification. We will focus on the guidelines for documenting functions, but note that they specify rules for documenting [classes](https://www.pythonlikeyoumeanit.com/module_4.html) and [modules](https://www.pythonlikeyoumeanit.com/Module5_OddsAndEnds/Modules_and_Packages.html#Modules).\n",
+ "\n",
+ "A function's docstring is divided up into several sections. Most of these sections are delimited with a header, e.g. \"Parameters\", followed by a horizontal line of dashes:\n",
+ "\n",
+ "```\n",
+ "Parameters\n",
+ "----------\n",
+ "```\n",
+ "\n",
+ "A docstring should at least consist of:\n",
+ "\n",
+ " - A succinct, single-line description of the function.\n",
+ " - An extended summary of the function, which provides a more verbose description of things.\n",
+ " - A `Parameters` section, which details the types of the input parameters along with descriptions of them. (This section is not necessary if your function is parameterless).\n",
+ " - A `Returns` section (or `Yields` for a generator), which details the object that is returned by the function. (This is not necessary if your function always returns `None`).\n",
+ "\n",
+ " \n",
+ "There are additional, optional sections that can be used to improve your documentation: \n",
+ "\n",
+ "- A `Notes` section, which can be used to discuss tertiary details of the function, such as a description of the algorithm that was used. You can also include mathematical formulae here, and cite primary materials\n",
+ "- A `References` section, used to document any works that were cited in the \"Notes\" section. It is not very common to need to reference primary sources in your docstrings.\n",
+ "- An `Examples` section, which contains console-style code, similar to the code snippets that you see in PLYMI, for using your function.\n",
+ "\n",
+ "There are additional, more obscure or technical sections that you can read about in the formal specification.\n",
+ "\n",
+ "The following function has a docstring that exemplifies all of these sections.\n",
+ "\n",
+ "```python\n",
+ "def pairwise_dists(x: np.ndarray, y: np.ndarray) -> np.ndarray:\n",
+ " \"\"\" Computes pairwise distances between the rows of ``x`` and ``y``.\n",
+ " \n",
+ " Returns the shape-(M, N) array of Euclidean distances between \n",
+ " the M rows of ``x`` and the N rows of ``y``.\n",
+ "\n",
+ " Parameters\n",
+ " ----------\n",
+ " x : numpy.ndarray, shape=(M, D)\n",
+ " An optional description of ``x``\n",
+ " y : numpy.ndarray, shape=(N, D)\n",
+ " An optional description of ``y``\n",
+ "\n",
+ " Returns\n",
+ " -------\n",
+ " numpy.ndarray, shape=(M, N)\n",
+ " The pairwise distances\n",
+ " \n",
+ " Notes\n",
+ " -----\n",
+ " This function uses the memory-efficient vectorized implementation that\n",
+ " is detailed in [1]_.\n",
+ " \n",
+ " References\n",
+ " ----------\n",
+ " .. [1] Soklaski, R. (2019, Jan 21). Array Broadcasting. \n",
+ " Retrieved from https://http://www.pythonlikeyoumeanit.com\n",
+ " \n",
+ " Examples\n",
+ " --------\n",
+ " Compute the pairwise distances between the rows of a shape-(3, 3) array \n",
+ " with the rows of a shape-(2, 3) array.\n",
+ " \n",
+ " >>> import numpy as np\n",
+ " >>> x = np.array([[1., 2., 3.],\n",
+ " ... [4., 5., 6.],\n",
+ " ... [7., 8., 9.]])\n",
+ " >>> y = np.array([[1., 2., 3.],\n",
+ " ... [4., 5., 6.]])\n",
+ " >>> pairwise_dists(x, y)\n",
+ " array([[ 0. , 5.19615242],\n",
+ " [ 5.19615242, 0. ],\n",
+ " [10.39230485, 5.19615242]])\n",
+ " \"\"\"\n",
+ " ...\n",
+ "```\n",
+ "\n",
+ "See that we specify **type information** in the `Parameters` and the `Returns` section of the docstring, even though it is redundant with the function signature's type-hints - it is useful to have this information in the docstring too. This is a good place to embellish the type information with pertinent information that is not conveyed by the formal type-hints. For example, we added shape information alongside the types in these sections. \n",
+ "\n",
+ "Variable names should be referenced using double back-ticks within the docstring; e.g. \\`\\`x\\`\\`. It should also be noted that that including an `Examples` section is very useful for creating high-quality documentation. It is recommended that you include examples liberally throughout your code. \n",
+ "\n",
+ "Here is another example docstring that adheres to the NumPy-style, one without a `Notes` and `References` section:\n",
+ "\n",
+ "```python\n",
+ "def compute_student_stats(grade_book: Dict[str, Iterable[float]],\n",
+ " stat_function: Callable[[Iterable[float]], Any],\n",
+ " student_list: Optional[List[str]] = None) -> List[Tuple[str, Any]]:\n",
+ " \"\"\" Computes custom statistics over students' grades.\n",
+ "\n",
+ " Applies ``stat_func`` over a list of each student's grades, \n",
+ " and accumulates name-stat tuple pairs in a list.\n",
+ " \n",
+ " Parameters\n",
+ " ----------\n",
+ " grade_book : Dict[str, List[float]]\n",
+ " The dictionary (name -> grades) of all of the students' \n",
+ " grades. \n",
+ "\n",
+ " stat_function: Callable[[Iterable[float]], Any]\n",
+ " The function used to compute statistics over each student's \n",
+ " grades.\n",
+ "\n",
+ " student_list : Optional[List[str]]\n",
+ " A list of names of the students for whom statistics will be \n",
+ " computed. By default, statistics will be computed for all \n",
+ " students in the grade book.\n",
+ "\n",
+ " Returns\n",
+ " -------\n",
+ " List[Tuple[str, Any]]\n",
+ " The name-stats tuple pair for each specified student.\n",
+ " \n",
+ " Examples\n",
+ " --------\n",
+ " >>> from statistics import mean\n",
+ " >>> grade_book = dict(Bruce=[90., 82., 92.], Courtney=[100., 85., 78.])\n",
+ " >>> compute_student_stats(grade_book, stat_function=mean)\n",
+ " [('Bruce', 88.0), ('Courtney', 87.66666666666667)]\n",
+ " \"\"\"\n",
+ " ...\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## The Google Documentation Style\n",
+ "\n",
+ "The Google documentation style is specified in full [here](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#doc-function-args); it is part of the complete [Google Python style guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#google-python-style-guide). The docstring style specified here is quite succinct in comparison to the NumPy spec. It consists of the following sections:\n",
+ "\n",
+ " - A succinct, single-line description of the function.\n",
+ " - An extended summary of the function, which provides a more verbose description of things.\n",
+ " - An `Args` section, which details the types of the input parameters along with descriptions of them. (This section is not necessary if your function is parameterless).\n",
+ " - A `Returns` section (or `Yields` for a generator), which details the object that is returned by the function. (This is not necessary if your function always returns `None`).\n",
+ " \n",
+ "There is also a `Raises` section in the case that your function raises exceptions under known conditions.\n",
+ "\n",
+ "Let's reproduce the docstrings for `pairwise_dists` and `compute_student_stats` using the Google style.\n",
+ "\n",
+ "```python\n",
+ "def pairwise_dists(x: np.ndarray, y: np.ndarray) -> np.ndarray:\n",
+ " \"\"\" Computes pairwise distances between the rows of ``x`` and ``y``.\n",
+ " \n",
+ " Returns the shape-(M, N) array of Euclidean distances between \n",
+ " the M rows of ``x`` and the N rows of ``y``.\n",
+ "\n",
+ " Args:\n",
+ " x (numpy.ndarray) : A shape-(M, D) array\n",
+ " y (numpy.ndarray) : A shape-(N, D) array\n",
+ "\n",
+ "\n",
+ " Returns:\n",
+ " (numpy.ndarray): A shape-(M, N) array of the pairwise distances\n",
+ " \"\"\"\n",
+ " ...\n",
+ "```\n",
+ "\n",
+ "```python\n",
+ "def compute_student_stats(grade_book: Dict[str, Iterable[float]],\n",
+ " stat_function: Callable[[Iterable[float]], Any],\n",
+ " student_list: Optional[List[str]] = None) -> List[Tuple[str, Any]]:\n",
+ " \"\"\" Computes custom statistics over students' grades.\n",
+ "\n",
+ " Applies ``stat_func`` over a list of each student's grades, \n",
+ " and accumulates name-stat tuple pairs in a list.\n",
+ " \n",
+ " Args:\n",
+ " grade_book (Dict[str, List[float]]): The dictionary \n",
+ " (name -> grades) of all of the students' grades. \n",
+ "\n",
+ " stat_function (Callable[[Iterable[float]], Any]): The \n",
+ " function used to compute statistics over each \n",
+ " student's grades.\n",
+ "\n",
+ " student_list (Optional[List[str]]): A list of names of \n",
+ " the students for whom statistics will be computed. \n",
+ " By default, statistics will be computed for all \n",
+ " students in the grade book.\n",
+ "\n",
+ " Returns\n",
+ " (List[Tuple[str, Any]]): The name-stats tuple pair for each \n",
+ " specified student.\n",
+ " \"\"\"\n",
+ " ...\n",
+ "```\n",
+ "\n",
+ "See that this style produces docstrings that are succinct, but are sometimes crowded horizontally given the successive levels of indentation within each docstring-section. \n",
+ "\n",
+ "It must also be noted that the [Napolean project](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/index.html#napoleon-marching-toward-legible-docstrings), which facilitates the automated generation of HTML documentation-pages from Python docstrings, has extended the Google specification to match the sections of the NumPy style. The Napolean version of the NumPy and Google docstring styles can be found [here](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/index.html#id1). This resource also includes useful examples of docstrings for modules and classes. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "**Takeaway:**\n",
+ "\n",
+ "Take some time to review the NumPy and Google docstring specifications, pick one, and stick to it faithfully when documenting your code. If you plan to code with others, find out if they have already chosen a documentation style. \n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "**Documentation Tools:**\n",
+ "\n",
+ "[Sphinx](http://www.sphinx-doc.org/en/master/) is a popular and hugely-powerful tool that will render documentation for your project in HTML by parsing the docstrings that are in your code. Python, NumPy, and almost all major 3rd party Python libraries use Sphinx to publish their documentation pages. [Napolean](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/index.html#napoleon-marching-toward-legible-docstrings) and [numpydoc](https://github.com/numpy/numpydoc) are Sphinx extensions that allows Sphinx to parse and nicely render docstrings that adhere to the NumPy and Google docstring specifications.\n",
+ "\n",
+ "[custom_inherit](https://github.com/meowklaski/custom_inherit) is a lightweight tool for [inheriting](http://www.pythonlikeyoumeanit.com/Module4_OOP/Inheritance.html) and merging docstrings. It provides rich support for merging the various sections of docstrings, and supports NumPy and Google-style docstrings natively. It also supports custom documentation styles.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Links to Official Documentation\n",
+ "\n",
+ "- [PEP 0: Index of Python Enhancement Proposals](https://www.python.org/dev/peps/)\n",
+ "- [PEP 1: Purpose and Guidelines](https://www.python.org/dev/peps/pep-0001/)\n",
+ "- [PEP 8: Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008)\n",
+ "- [PEP 483: The Theory of Type Hints](https://www.python.org/dev/peps/pep-0483/)\n",
+ "- [PEP 484: Type-Hinting](https://www.python.org/dev/peps/pep-0484/)\n",
+ "- [PEP 526: Syntax for Variable Annotations](https://www.python.org/dev/peps/pep-0526/)\n",
+ "- [The standard library's typing module](https://docs.python.org/3/library/typing.html)\n",
+ "- [Definition: abstract base class](https://docs.python.org/3/glossary.html#term-abstract-base-class)\n",
+ "- [NumPy Documentation Specification](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard)\n",
+ "- [Google Documentation Specification](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#doc-function-args)\n",
+ "- [Napolean: Marching Towards Legible Docstrings](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/index.html#)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Reading Comprehension Solutions"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Type Hinting: Solutions**\n",
+ "\n",
+ "The following is a well-annotated version of `get_first_and_last`:\n",
+ "\n",
+ "```python\n",
+ "from typing import Sequence, Tuple, Any\n",
+ "\n",
+ "def get_first_and_last(x: Sequence[Any]) -> Tuple[Any, Any]:\n",
+ " \"\"\" Returns the first and last elements of `x`. `x` is \n",
+ " assumed to be non-empty\n",
+ " \"\"\" \n",
+ " return (x[0], x[-1])\n",
+ "```\n",
+ "\n",
+ "This function only requires that `x` can be indexed into. Referring to the [abc's for Python's collections](https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes), `Sequence` is the simplest abstract base class that supports the `__getitem__` syntax. Indeed, this was a trait characteristic that we saw for all of [Python's sequence types](https://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/SequenceTypes.html). Lastly, note that we make no assumptions about the contents of `x`, thus we use the generic `Any` type. "
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/Python/module_5.rst b/Python/module_5.rst
index e02d4c8b..2eeee618 100644
--- a/Python/module_5.rst
+++ b/Python/module_5.rst
@@ -2,7 +2,10 @@ Module 5: Odds and Ends
=====================================
This module contains materials that are extraneous to the essentials of Python as a language and of NumPy, but are nonetheless critical to doing day-to-day work using these tools.
-The first section introduces matplotlib, a library that allows us to plot and visually inspect data. Here, we will specifically learn how to leverage matplotlib's object-oriented API, as opposed to its functional API, for creating scatter plots, line plots, histograms, and image plots.
+The first section introduces some general guidelines for writing "good code". Specifically, it points you, the reader, to a style guide that many people in the Python community abide by. It also introduces a relatively new and increasingly-popular feature of Python, called type-hinting, which permits us to enhance our code with type-documentation annotations. The reader will also be introduced to NumPy's and Google's respective specifications for writing good docstrings.
+
+
+The second section of this module intoduces matplotlib, a library that allows us to plot and visually inspect data. Here, we will specifically learn how to leverage matplotlib's object-oriented API, as opposed to its functional API, for creating scatter plots, line plots, histograms, and image plots.
The next section presents the "best practices" for working with files in Python. This includes reading from and writing to files within a context manager. We will learn to leverage the powerful :code:`pathlib.Path` class to work with paths in elegant and platform-independent ways. Finally, we review some critical file utilities, like searching for files with :code:`glob`, saving files with :code:`pickle`, and saving NumPy arrays.
@@ -14,6 +17,7 @@ More sections will be added to this module down the road.
:maxdepth: 2
:caption: Contents:
+ Module5_OddsAndEnds/Writing_Good_Code.ipynb
Module5_OddsAndEnds/Matplotlib.ipynb
Module5_OddsAndEnds/WorkingWithFiles.ipynb
Module5_OddsAndEnds/Modules_and_Packages.ipynb