A document from MCS 275 Spring 2023, instructor David Dumas. You can also get the notebook file.

MCS 275 - Spring 2023 - David Dumas

Coding standards

Enforcement

To receive full credit, Python code submitted for a graded MCS 275 assignment must follow the rules in this document.

There are several rules, and we know it may take time to learn them. The level of enforcement will be increased over time, with feedback but no point deduction for most infractions of these rules on the first homework.

Requirements

This is the only section of the document you absolutely need to read. The rest of the document might help you understand these rules better, but the rules themselves are just:

  • CS1 - Header: The first few lines of the main Python file of any assignment must be comment lines (i.e. start with #), and these lines must contain this information:

    • The course (MCS 275 Spring 2023) and assignment (e.g. Homework 5 or Project 3)
    • Your name
    • A statement about whether you are the author of the work, and whether you followed the rules in the syllabus. (Sometimes you will be given template code to modify and submit, in which case the expected answer is that you are the author of the changes to the provided template.)
  • CS2 - Docstrings: Every function, module, and class must have a descriptive docstring (a string literal, not a comment, that is the first statement in a module, class body, or function body). Descriptive means that it explains what the code does (without much concern for how it does it).

  • CS3 - Comments: Explanatory text is included, when appropriate, in the form of comment lines (starting with #) and not in any other way. For example, including string literals with explanatory text is not acceptable unless the string literal is a docstring. Comments are appropriate to explain why and how things are done.

  • CS4 - Names: Names of functions, variables, and classes should be descriptive and not abbreviated to the point of incomprehensibility. Iterator variables used in a short loop or comprehension are exceptions, where single character or abbreviated names are permitted.

  • CS5 - Indenting: Each indented code block should be indented by the same amount relative to the surrounding code, and that amount must be at least two spaces (preferred) or one tab (discouraged).

  • CS6 - Avoid pointless conversions: Don’t use str() on something that is already a string, don't use int() on something that is already an integer, don’t convert a string to a list just to index it (strings already support indexing!), etc.

  • CS7 - Forbidden range(len()): In most cases, if range(len(...)) appears in your code, there is a preferred alternative that uses Python's iteration features in a more natural way that you must use instead. If you think you are in one of the rare situations where range(len(...)) should appear, ask the instructor or TA for advice before proceeding.

Examples and discussion

The list of requirements from Section 2 is duplicated here, with a rationale for each rule and at least one code example.

CS1 - Header

The first few lines of the main Python file of any assignment must be comment lines (i.e. start with #), and these lines must contain this information: * The course (MCS 275 Spring 2023) and assignment (e.g. Homework 5 or Project 3) * Your name * A statement about whether you are the author of the work, and whether you followed the rules in the syllabus. (Sometimes you will be given template code to modify and submit, in which case the expected answer is that you are the author of the changes to the provided template.)

Why we have this rule

Several reasons:

  • It is helpful to have this header in your source file to avoid any accidental confusion between different students' submissions during the grading and testing process. We take other steps to avoid that as well, but this label is an extra layer of protection against mistakes.

  • We want the existence of rules about collaboration, use of external resources, etc., to be evident in every aspect of the course, to avoid surprises. Asking you to write a statement about following those rules builds a quick reminder of this into each assignement.

Compliant example

For a Python file written by a student from scratch. It includes a docstring after the header, too, and while the header includes "metadata" like the student name and assignment number, the docstring is the only part that is a description of the file contents.

In [ ]:
# MCS 275 Spring 2023 Homework 38
# Marie Curie
# I am the sole author of this program, and I followed the rules given
# in the assignment and in the course syllabus.
"""
Program to convert electrical discharge measurements into
estimate of radioactivity of a sample.
"""

Compliant example

For an assignment where the course staff provide some kind of template code that students are expected to modify or add to.

In [ ]:
# MCS 275 Spring 2023 Homework 38
# Emmy Noether
# I am the sole author of the changes to the template that was provided
# by course staff, and I followed the rules in the course syllabus and assignment.
"""
Given a symmetry of a physical system, find an expression for the
corresponding conserved quantity.
"""

CS2 - Docstrings

Every function, module, and class must have a descriptive docstring (a string literal, not a comment, that is the first statement in a module, class body, or function body). Descriptive means that it explains what the code does (without much concern for how it does it).

Why we have this rule

Documentation is extremely important in computer programming. Being able to explain your work and ideas in written form is broadly important throughout computer science (indeed all of science).

Python has a dedicated feature for connecting a short documentation string (docstring) with logical units of a program (modules, classes, functions) in a way that is understood and used by the Python interpreter. For example, the docstring of a module, class, or function will be shown if you ask the interpreter for help() on an object. Always including docstrings is a good practice, and one that is helpful for anyone who reads your code (e.g. course staff).

Non-compliant example

This function lacks a docstring.

In [ ]:
def prefix(s):
    # <-- PROBLEM HERE
    return s[:3]

Compliant example

This modified version is acceptable.

In [ ]:
def prefix(s):
    """Return the first three characters of a string ‘s‘"""
    return s[:3]

Non-compliant example

If the content of a .py file is as shown below, then it lacks a module docstring and is thus not compliant. It does have function docstrings, but not an overall docstring for the module.

In [ ]:
# Contents of a module that has function docstrings but no module docstring
# <-- PROBLEM HERE

def lerp(a,b,t):
    """
    Linearly interpolate between `a` and `b` at `t=0` and `t=1` respectively, but
    cap the return values at `a` for `t<0` and `b` for `t>1`.
    """
    if t<0:
        return a
    elif t>1:
        return b
    else:
        return (1-t)*a + t*b

def smooth_lerp(a,b,t):
    """
    Interpolate between `a` and `b` at `t=0` and `t=1` respectively, but
    cap the return values at `a` for `t<0` and `b` for `t>1`.  Use an 
    interpolation that has a slow start and gradual stop.
    """
    if t<0:
        return a
    elif t>1:
        return b
    else:
        return lerp(a,b,3*t**2 - 2*t**3)

Compliant example

This modified version is acceptable.

In [ ]:
# Contents of a module.  The next line is the docstring!
"Single-variable two-point interpolation schemes"


def lerp(a,b,t):
    """
    Linearly interpolate between `a` and `b` at `t=0` and `t=1` respectively, but
    cap the return values at `a` for `t<0` and `b` for `t>1`.
    """
    if t<0:
        return a
    elif t>1:
        return b
    else:
        return (1-t)*a + t*b

def smooth_lerp(a,b,t):
    """
    Interpolate between `a` and `b` at `t=0` and `t=1` respectively, but
    cap the return values at `a` for `t<0` and `b` for `t>1`.  Use an 
    interpolation that has a slow start and gradual stop.
    """
    if t<0:
        return a
    elif t>1:
        return b
    else:
        return lerp(a,b,3*t**2 - 2*t**3)

CS3 - Comments

Explanatory text is included, when appropriate, in the form of comment lines (starting with #) and not in any other way. For example, including string literals with explanatory text is not acceptable unless the string literal is a docstring. Comments are appropriate to explain why and how things are done.

Why we have this rule

Python has a specific way to include comments, so that should be the one that is used. String literals that are not used in any way will be ignored by the interpreter, but may make a program a bit slower to start up. Also, string literals and comments are styled in different ways by most code editors (e.g. different colors or typfaces). By keeping explanatory text in comments, such syntax highlighting will its intended effect of making functional code and explanatory text visually distinct.

Non-compliant example

This loop is not acceptable, because it uses a string literal instead of a comment. (It isn’t a docstring, because loops do not have docstrings.)

In [ ]:
for item in container:
    """Display the weight of this item (units will be added by the item.weight object)"""  # <-- PROBLEM HERE
    print(item.weight)

Compliant example

This modified version is acceptable.

In [ ]:
for item in container:
    # Display the weight of this item (units will be added by the item.weight object)
    print(item.weight)

CS4 Names:

Names of functions, variables, and classes should be descriptive and not abbreviated to the point of incomprehensibility. Iterator variables used in a short loop or comprehension are exceptions, where single character or abbreviated names are permitted.

Why we have this rule

It is immensely more difficult to understand and evaluate programs where objects have non-descriptive names. Moreover, it is easier to make mistakes while writing or modifying programs that use poor names.

Non-compliant example

This code defines two variables (presumably for later use) whose names give no clear indication of their purpose or contents.

In [ ]:
L1 = [ i+1 for i in range(8) ] # <-- PROBLEM HERE
LL = [ i-4 for i in L1 ]       # <-- PROBLEM HERE
Lsp1 = [ x*x+1 for x in L1 ]   # <-- PROBLEM HERE

Compliant example

This modified version is acceptable.

In [ ]:
whole_number_range = [ i+1 for i in range(8) ]
shifted_range = [ i-4 for i in whole_number_range ]
squares_plus_one = [ x*x+1 for x in whole_number_range ]

Compliant example

This modified version is also acceptable, and uses list comprehensions to make the code shorter and more readable.

In [ ]:
shifted_range = [ i-3 for i in range(8) ]
squares_plus_one = [ (i+1)*(i+1)+1 for i in range(8) ]

CS5 Indenting

Each indented code block should be indented by the same amount relative to the surrounding code, and that amount must be at least two spaces (preferred) or one tab (discouraged).

Why we have this rule

Inconsistently indented code, or code that uses only one space to indent blocks, is difficult to read.

Some syntax highlighting systems also fail when different amounts of indenting are used.

Finally, some variations in indenting are Python syntax errors.

Non-compliant example

This code example uses various indent lengths, including one below 2 spaces.

In [ ]:
# The problem in this example is the *different* indent lengths in various blocks

def is_alien_invasion():
  "Determine whether aliens appear to be invading"   # <-- indented 2 spaces
  return infrastructure_compromised() and unrecognized_life_forms()

if is_alien_invasion:
 print("Welcome, space wasp overlords!")  # <-- indented 1 space
else:
     print("Welcome to MCS 275.")  # <-- indented 5 spaces

Compliant example

Make all indent lengths 4 spaces.

In [ ]:
def is_alien_invasion():
    "Determine whether aliens appear to be invading"
    return infrastructure_compromised() and unrecognized_life_forms()

if is_alien_invasion:
    print("Welcome, space wasp overlords!")
else:
    print("Welcome to MCS 275.")

CS6 - Avoid pointless conversions

Don’t use str() on something that is already a string, don't use int() on something that is already an integer, don’t convert a string to a list just to index it (strings already support indexing!), etc.

Why we have this rule

Pointless conversions perpetuate uncertainty about return types and allowed operations that are benficial to resolve early in the process of learning a programming language. Also, removing pointless conversions makes programs easier to read and understand.

Non-compliant example

The return value of input() is already a string, so there's no need to convert it.

In [ ]:
s = str(input("Enter a line of text: "))

Compliant example

In [ ]:
s = input("Enter a line of text: ")

Non-compliant example

Strings support indexing, so there is no need to convert to a list just to get a character by index.

In [ ]:
s = "Python strings support indexing!"
L = list(s) # <-- PROBLEM HERE
print(L[5],L[11])

Compliant example

In [ ]:
s = "Python strings support indexing!"
print(s[5],s[11])

In most cases, if range(len(...)) appears in your code, there is a preferred alternative that uses Python's iteration features in a more natural way that you must use instead. If you think you are in one of the rare situations where range(len(...)) should appear, ask the instructor or TA for advice before proceeding.

CS7 - Forbidden range(len())

In most cases, if range(len(...)) appears in your code, there is a preferred alternative that uses Python's iteration features in a more natural way that you must use instead. If you think you are in one of the rare situations where range(len(...)) should appear, ask the instructor or TA for advice before proceeding.

Why we have this rule

Python has features that make it easy to iterate over all the values in a container (such as a list, set, or all the keys of a dict) without using explicit integer indices. Using these features is not just a convenience, but a good design practice, because it allows the container to control the iteration process. It also usually results in code that is shorter and easier to read.

Non-compliant example

Using range(len(...)) to generate integer indices just so we can visit each element of a list using a for loop.

In [ ]:
for i in range(len(users)):   #<-- PROBLEM HERE
    print("Username:",users[i])

Compliant example

This correction instead uses the fact that Python list objects are iterable---they can appear after in in a for loop, and yield each of their elements in order.

In [ ]:
for user in users:
    print("Username:",user)

Non-compliant example

In [ ]:
for i in range(len(users)):  #<-- PROBLEM HERE
    print("User",i,"has username",users[i])

Compliant example

One way to fix this example is to use enumerate to generate a sequence of pairs index, item from the list. This brings the benefit that an ordered container (like list) could be replaced by an unordered one (like set) and the loop would still work.

In [ ]:
for i,username in enumerate(users):
    print("User",i,"has username",username)

Additional code style requests

These are suggestions, not requirements, but they encourage some good habits and code style.

  • Use exactly four spaces for indenting code blocks.
  • Use either " or """ to delimit a string, unless the string itself contains the double quote character. (In general, using one string delimiter consistently is more important than whether you use single or double quote characters.)
  • Limit lines of code to a length of 100 characters.
  • Avoid the single-letter variable names l (lower case letter ell), O (upper case letter oh), I (upper case letter eye) which are easy to confuse with other symbols in some typefaces.
  • Rather than relying on your memory of the rules, before your final submission of any assignment, look through your code with this document open and check each rule in sequence, e.g. read through to check for (CS1), then (CS2), etc.

Note about future changes to this document

Most likely, this document will not change during the course. However, if this document is updated, an announcement will be made on the course web page and in lecture. Rules added or changed will only apply to assignments or projects published after the update to this document appears. The list of changes made to this document and the dates they appeared can be found below.

Revision history

  • 2023-01-06 Initial publication