Python Style Guide for CS121

This document draws very heavily from John Magee’s Python Style Guide: simplified version for beginner programmers and the Style Guide for Python Code by Guido van Rossum and Barry Warsaw.

Introduction

This simplified style guide is intended to help beginning Python programmers adhere to basic coding conventions. Properly styled computer code is more easily read and understood by humans. You may revisit code you write later, or you may work on code with other people in the future. It’s important that your code is easily understood by yourself and others. This guide is based on John Magee’s Python Style Guide: simplified version for beginner programmers, which in turn was based on Style Guide for Python Code by Guido van Rossum and Barry Warsaw. The original version of Style for Python Code has been placed in the public domain by the authors and is available at: http://www.python.org/dev/peps/pep-0008/. Any errors, omissions, or odious style conventions should be blamed on us.

Consistency

Code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Python code.

A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important.

But most importantly: know when to be inconsistent – sometimes the style guide just doesn’t apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don’t hesitate to ask!

Two good reasons to break a particular rule:

  1. If applying the rule would make the code less readable, even for someone who is used to reading code that follows the rules.
  2. To be consistent with surrounding code that also breaks it (maybe for historic reasons) – although this situation does provide opportunity to clean up someone else’s mess.

Code lay-out

Indentation

Use 4 spaces per indentation level.

Never use “tab characters”.

Note that you can configure most text editors to insert 4 spaces whenever you press the tab key. To check whether your editor is properly configured, try the following: press the tab key, and then press the left arrow key. If the cursor jumps to the beginning of the line, your editor inserted a single “tab character”. If the cursor only goes back one space (and you have to press the left arrow key three more times to get to the start of the line), then the editor is correctly inserting four spaces.

Whether to use tabs or spaces is one of the many “religious wars” within the programming community (the most notable example being the editor war). So, you may run into people (even people here on campus) who will feel very strongly about how tabs are better than spaces, and they may even present to you a number of cogent arguments in favor of using tabs.

Do not listen to these people. They are wrong and cannot be trusted.

Maximum Line Length

Limit all lines to a maximum of 79 characters. There are still many devices around that are limited to 80 character lines; plus, limiting windows to 80 characters makes it possible to have several windows side-by-side. The default wrapping on such devices disrupts the visual structure of the code, making it more difficult to understand. Therefore, please limit all lines to a maximum of 79 characters.

The preferred way of wrapping long lines is by using Python’s implied line continuation inside parentheses, brackets and braces. If necessary, you can add an extra pair of parentheses around an expression, but sometimes using a backslash looks better.

Blank Lines

Separate function and class definitions with two blank lines.

Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).

Use blank lines in functions, sparingly, to indicate logical sections.

Imports

Placement

Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.

Formatting

Imports should usually be on separate lines, e.g.:

Yes:

import os
import sys

No:

import sys, os

It is okay to use this form though:

from subprocess import Popen, PIPE

Whitespace in Expressions and Statements

Avoid extraneous whitespace in the following situations:

  • Immediately inside parentheses, brackets or braces.

    Yes: spam(ham[1], {eggs: 2})
    No:  spam( ham[ 1 ], { eggs: 2 } )
    
  • Immediately before a comma or a colon:

    Yes: if x == 4: print x, y
    No:  if x == 4 : print x , y
    
  • Immediately before the open parenthesis that starts the argument list of a function call:

    Yes: spam(1)
    No:  spam (1)
    
  • Immediately before the open parenthesis that starts an indexing or slicing:

    Yes: dict['key'] = list[index]
    No:  dict ['key'] = list [index]
    
  • More than one space around an assignment (or other) operator to align it with another.

Yes:

x = 1
y = 2
long_variable = 3

No:

x             = 1
y             = 2
long_variable = 3

Places to use spaces

  • Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc. ), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), Booleans (and, or, not).
  • Use spaces around arithmetic operators:

Yes:

i = i + 1
submitted += 1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

No:

i=i+1
submitted +=1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
  • Compound statements (multiple statements on the same line) are generally discouraged.

Yes:

if foo == 'blah':
    do_blah_thing()
do_one()
do_two()
do_three()

Rather not:

if foo == 'blah': do_blah_thing()
do_one(); do_two(); do_three()

Comments

Comments that contradict the code are worse than no comments. Always make a priority of keeping the comments up-to-date when the code changes! Comments should consist of either a single short phrase or one or more complete sentences. The first word of a comment should be capitalized, unless it is an identifier that begins with a lower case letter (never alter the case of identifiers!).

If a comment is short, the period at the end can be omitted. Block comments generally consist of one or more paragraphs built out of complete sentences, and each sentence should end in a period.

Header Comments

Header comments appear at the top of a file. These lines typically include the filename, author, date, version number, and a description of what the file is for and what it contains. For class assignments, headers should always include your name!

# One line description of the contents of the file.
#
# YOUR NAME
#
# Usage information.

Function Comments

Function comments should be done in the form of a docstring, i.e., a multi-line string (delimited by triple quotes, ''') after the function header.

This docstring must contain information specific to what a function does. It should also include a description of the purpose and expected input arguments, the expected output values, and how error conditions are handled.

Example:

def hypotenuse(a, b):
    '''
    This function solves Pythagorean theorem a^2 + b^2 = c^2
    for the value of c.

    a, b: the lengths of sides of a right triangle.

    returns: the length of the hypotenuse.
    '''

    return math.sqrt(a**2 + b**2)

Block Comments

Block comments generally apply to some (or all) code that follows them, and are indented to the same level as that code. Each line of a block comment starts with a # and a single space (unless it is indented text inside the comment). Paragraphs inside a block comment are separated by a line containing a single #.

Inline Comments

Use inline comments sparingly. An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space.

Inline comments are unnecessary and in fact distracting if they state the obvious. Don’t do this:

x = x + 1                 # Increment x

But sometimes, this style of comment is useful:

x = x + 1                 # Compensate for border

Naming Conventions

There are various naming conventions used in Python and other programming languages. The two common ones we will see are lowercase_with_underscore (aka snake_case) and CamelCase. For consistency, we will stick to the snake case convention. Most multi-word names should start with a lowercase letter and the words should be separated by underscores. Here are some examples:

sum_of_squares
print_happy_birthday
total_apples

One exception: class names should start with a capital letter and use CamelCase. For example, here the the class names from a few classes that you will use in Lab #5:

DivvyData
DivvyTrip
DivvyStation
Route

Use descriptive names for parameter names, global variables, function names, class names, and module names. Use short names for local variables. In general, the further away a variable will be used, the more descriptive the name needs to be.

Yes:

for x in data:
    print(f(x))

No:

for element_of_list in data:
    print(f(element_of_list))

The names of functions that perform an action should include a verb:

Yes: read_column_from_csv
No:  column_from_csv

Avoid Magic Numbers

Avoid sprinkling numbers that will have very little meaning to your reader throughout your code. Here’s an example from Lab #5:

Yes:

if abs(d-expected) >= TOLERANCE:
    s = "WRONG: Expected distance between {} and {} to be {:.2f} {}"
    s = s + "but got {:.2f} {}"
    print(s.format(c1, c2, expected/scale, unit, d/scale, unit))

No:

if abs(d-expected) >= 10:
    s = "WRONG: Expected distance between {} and {} to be {:.2f} {}"
    s = s + "but got {:.2f} {}"
    print(s.format(c1, c2, expected/scale, unit, d/scale, unit))

Use ALL_CAPS for constants:

Yes: TOLERANCE = 10
No:  tolerance = 10

Programming recommendations

Do not compare boolean values to True or False using ==.

Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:

Be consistent in return statements. Either all return statements in a function should return an expression, or none of them should. If any return statement returns an expression, any return statements where no value is returned should explicitly state this fact as return None , and an explicit return statement should be present at the end of the function (if reachable).

Yes:

def foo(x):
    if x >= 0:
        return math.sqrt(x)
    else:
        return None

def bar(x):
    if x < 0:
        return None
    return math.sqrt(x)

No:

def foo(x):
    if x >= 0:
        return math.sqrt(x)

def bar(x):
    if x < 0:
        return
    return math.sqrt(x)

For sequences, (strings, lists, tuples), use the fact that empty sequences are false.

Yes: if not seq:
     if seq:

No:  if len(seq)
     if not len(seq)