Return to Blog

Using Python's built-in help system

By John Lekberg on January 29, 2020.


Python has a built-in help system which you can activate using the help function:

help()
Welcome to Python 3.7's help utility!

If this is your first time using Python, you should
definitely check out the tutorial on the Internet at
https://docs.python.org/3.7/tutorial/.

Enter the name of any module, keyword, or topic to get help
on writing Python programs and using Python modules.  To
quit this help utility and return to the interpreter, just
type "quit".

...

In this week's Python blog post, you will learn

When do I use the help system?

Python has great online documentation which covers the same topics as the help system, so when do I use the help system instead of the online documentation?

Using the help system on unfamiliar code

Here is a short story about using the help system to understand my friend's code.

I'm working on a programming challenge with my friend Mia 👩🏻 . The challenge is to design a function that returns the sum of the first n Fibonacci numbers. I'm having trouble with this, so I ask Mia how she solved the problem. She shows me her code:

import itertools

def fibonacci():
    a, b = 1, 1
    while True:
        yield a
        a, b = b, a + b

def sum_fib(n):
    return sum(itertools.islice(fibonacci(), n))

sum_fib(100)
927372692193078999175

I tell her that there are 3 things I don't understand:

  1. What does yield do?
  2. What does a, b = b, a + b mean?
  3. What does the itertools.islice function do?

Mia laughs and tells me to figure it out for myself, so I boot up the Python interpreter and start the interactive help system:

$ python3
>>> help()
Welcome to Python 3.7's help utility!

If this is your first time using Python, you should
definitely check out the tutorial on the Internet at
https://docs.python.org/3.7/tutorial/.

...
help>

What does yield do?

help> yield
The "yield" statement
*********************

...

Yield expressions and statements are only used when defining
a *generator* function, and are only used in the body of the
generator function.  Using yield in a function definition is
sufficient to cause that definition to create a generator
function instead of a normal function.

...

(Yield expressions define generator functions.)

What does a, b = b, a + b do? Searching = doesn't help:

help> =
No Python documentation found for '='.

I check which topics the help system covers:

help> topics
Here is a list of available topics.  Enter any topic name to
get more help.

ASSERTION           DELETION            LOOPING       
ASSIGNMENT          DICTIONARIES        MAPPINGMETHODS
ATTRIBUTEMETHODS    DICTIONARYLITERALS  MAPPINGS      
...

I read more about assignment:

help> ASSIGNMENT
Assignment statements
*********************

...

An assignment statement evaluates the expression list
(remember that this can be a single expression or a
comma-separated list, the latter yielding a tuple) and
assigns the single resulting object to each of the target
lists, from left to right.

...

The object must be an iterable with the same number of items
as there are targets in the target list, and the items are
assigned, from left to right, to the corresponding targets

...

I learn that multiple assignment

a, b = b, a + b

is just like

x = (b, a + b)
a = x[0]
b = x[1]

(This is an easy way to swap names: x, y = y, x.)

What does itertools.islice do?

help> itertools.islice
Help on class islice in itertools:

itertools.islice = class islice(builtins.object)
 |  islice(iterable, stop) --> islice object
 |  islice(iterable, start, stop[, step]) --> islice object
 |
 |  ...
 |  
 |  Works like a slice() on a list but returns an iterator.
 |
 |  ...

For Mia's code, itertools.islice takes the first n elements generated by the fibonacci() generator function.

I understand how Mia's code works now, so I explain it back to her. She tells me "good job" 🤷🏻‍♀️ and returns to her computer, solving the next programming challenge.

Making my own code available to the help system

Here is a short story about making my code work with the help system, in order to share my code with Mia 👩🏻 .

I wrote a function that creates a random binary tree and I want to share this with Mia. She wrote some algorithms that process binary trees and she wants to test them out on randomly generated trees instead of handwritten cases.

Here's my code:

from collections import namedtuple
import random

# Represents a node in a binary tree.
#
# None is used to represent absence of a branch, so a
# leaf node looks like Tree(x, None, None).
Tree = namedtuple("Tree", ["value", "left", "right"])

def random_tree(max_depth=1):
    # Generate a random binary tree of depth at most
    # max_depth.
    #
    # Each node has a value between 10 and 99. Each node
    # (excluding nodes at the maximum depth) has an 80%
    # chance of having a left child and an 80% chance of
    # having a right child.

    if max_depth == 0:
        return

    value = random.randint(10, 99)

    left = right = None
    if random.random() < 0.8:
        left = random_tree(max_depth-1)
    if random.random() < 0.8:
        right = random_tree(max_depth-1)

    return Tree(value, left, right)

The problem is that my comments don't show up when I ask the help system about Tree and random_tree:

help(Tree)
Help on class Tree in module __main__:

class Tree(builtins.tuple)
 |  Tree(value, left, right)
 |
 |  Tree(value, left, right)
 |
 |  Method resolution order:
 |      Tree
 |      builtins.tuple
 |  ...
help(random_tree)
Help on function random_tree in module __main__:

random_tree(max_depth=1)

I want Mia to be able to use the help system to learn about the code I wrote, so I turn the comments into docstrings (PEP 257). Docstrings are strings that are attached to objects with the __doc__ attribute. Unlike comments, docstrings aren't thrown away by the Python Interpreter; they are kept around and used by the help system. I add a docstring to random_tree as the first statement in the function:

def random_tree(max_depth=1):
    """Generate a random binary tree of depth at most
    max_depth.
    
    Each node has a value between 10 and 99. Each node
    (excluding nodes at the maximum depth) has an 80% chance
    of having a left child and an 80% chance of having a
    right child.
    
    """
    if max_depth == 0:
        return

    value = random.randint(10, 99)

    left = right = None
    if random.random() < 0.8:
        left = random_tree(max_depth-1)
    if random.random() < 0.8:
        right = random_tree(max_depth-1)

    return Tree(value, left, right)

(PEP 257 recommends using triple double quotes """ for docstrings.)

Now the help system includes my comments for random_tree:

help(random_tree)
Help on function random_tree in module __main__:

random_tree(max_depth=1)
    Generate a random binary tree of depth at most
    max_depth.
    
    Each node has a value between 10 and 99. Each node
    (excluding nodes at the maximum depth) has an 80% chance
    of having a left child and an 80% chance of having a
    right child.

I add a docstring to Tree by directly assigning __doc__:

Tree = namedtuple("Tree", ["value", "left", "right"])
Tree.__doc__ = """Represents a node in a binary tree.

None is used to represent absence of a branch, so a
leaf node looks like Tree(x, None, None).
"""

Now the help system includes my comments for Tree:

help(Tree)
Help on class Tree in module __main__:

class Tree(builtins.tuple)
 |  Tree(value, left, right)
 |
 |  Represents a node in a binary tree.
 |
 |  None is used to represent absence of a branch, so a
 |  leaf node looks like Tree(x, None, None).
 |
 |  Method resolution order:
 |  ...

I share my code with Mia and she's able to figure out how it works and how to use it without needing to ask me. I tell Mia "good job" 🤷🏼‍♂️ and get back to solving the next programming challenge.

In conclusion...

Python has a built-in help system that can help you understand other people's code, as well as allow you to document your code for other people. It covers the same content as Python's online documentation but can be more useful when you want to learn about something that's hard to Google or when you want to learn about the results of a function without needing to pore over API documentation.

Python's docstring allow any kind of plain text comments to be written, but Python has special support for the reStructuredText format. My challenge to you is:

Read through PEP 287, "reStructuredText Docstring Format", to learn about using reStructuredText in docstrings. Then, document a Python function using reStructuredText. (Pick a function from the standard library or use the random_tree function that I shared with Mia.)

If you enjoyed this week's post, share it with your friends and stay tuned for next week's post. See you then!