Overview

Teaching: 10 min
Exercises: 15 min
Questions
  • How can I make my programs more readable?

  • How do most programmers format their code?

Objectives
  • Provide sound justifications for basic rules of coding style.

  • Refactor one-page programs to make them more readable and justify the changes.

  • Use Python community coding standards (PEP-8).

Follow standard Python style in your code.

Use docstrings to provide online help.

def average(values):
    "Return average of values, or None if no values are supplied."

    if len(values) == 0:
        return None
    return sum(values) / average(values)

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

average(values)
    Return average of values, or None if no values are supplied.

Multiline Strings

Often use multiline strings for documentation. These start and end with three quote characters (either single or double) and end with three matching characters.

"""This string spans
multiple lines.

Blank lines are allowed."""

What Will Be Shown?

Highlight the lines in the code below that will be available as online help. Are there lines that should be made available, but won’t be? Will any lines produce a syntax error or a runtime error?

"Find maximum edit distance between multiple sequences."
# This finds the maximum distance between all sequences.

def overall_max(sequences):
    '''Determine overall maximum edit distance.'''

    highest = 0
    for left in sequences:
        for right in sequences:
            '''Avoid checking sequence against itself.'''
            if left != right:
                this = edit_distance(left, right)
                highest = max(highest, this)

    # Report.
    return highest

Document This

Turn the comment on the following function into a docstring and check that help displays it properly.

def middle(a, b, c):
    # Return the middle value of three.
    # Assumes the values can actually be compared.
    values = [a, b, c]
    values.sort()
    return values[1]

Clean Up This Code

  1. Read this short program and try to predict what it does.
  2. Run it: how accurate was your prediction?
  3. Refactor the program to make it more readable. Remember to run it after each change to ensure its behavior hasn’t changed.
  4. Compare your rewrite with your neighbor’s. What did you do the same? What did you do differently, and why?
import sys
n = int(sys.argv[1])
s = sys.argv[2]
print(s)
i = 0
while i < n:
    # print('at', j)
    new = ''
    for j in range(len(s)):
        left = j-1
        right = (j+1)%len(s)
        if s[left]==s[right]: new += '-'
        else: new += '*'
    s=''.join(new)
    print(s)
    i += 1

Here’s one solution.

def string_machine(input_string, iterations):
    """
    Takes input_string and generates a new string with -'s and *'s
    corresponding to characters that have identical adjacent characters
    or not, respectively.  Iterates through this procedure with the resultant
    strings for the supplied number of iterations.
    """
    print(input_string)
    old = input_string
    for i in range(iterations):
        new = ''
        # iterate through characters in previous string
        for j in range(len(s)):
            left = j-1
            right = (j+1)%len(s) # ensure right index wraps around
            if old[left]==old[right]:
                new += '-'
            else:
                new += '*'
        print(new)
        # store new string as old
        old = new

string_machine('et cetera', 10)

Finding Neighbors

This function is supposed to find the minimum value adjacent to (but not in) a specified location in an array. For what inputs does it produce the wrong answer? How can it be repaired?

def any_negative_neighbors(array, i, j, use_diagonals):
    '''
    Return True if any neighbors of (i,j) are negative, or False if none are.
    Only check diagonal neighbors if use_diagonals is True.
    '''

    width, height = array.shape

    if i > 0 and array[i-1, j] < 0: return True
    if i < width and array[i+1, j] < 0: return True
    if j > 0 and array[i, i-1] < 0: return True
    if j < width and array[i, j+1] < 0: return True

    if not use_diagonals: return False

    if i > 0 and j > 0 and array[i-1, j-1] < 0: return True
    if i > 0 and j < width and array[i-1, j+1] < 0: return True
    if i < width and j > 0 and array[i+1, j-1] < 0: return True
    if i < width and j < height and array[i+1, j+1] < 0: return True

Key Points