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

MCS 275 - Spring 2024 - Emily 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 three lines of the main Python file of any assignment must be comment lines (i.e. must start with #), and these lines must contain this information:

    • Line 1: The course and assignment (e.g. MCS 275 Spring 2024 Homework 5)
    • Line 2: Your full name
    • Line 3: A statement accurately describing the authorship and/or sources of the work (which under the course rules should either be that you wrote it from scratch, or that we provided some template and that you are the sole author of the changes made to that template).
  • CS2 - Comments and docstrings: 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 clarify why and how things are done. Docstrings (meaning a string literal that appears as the first statement inside a function, module, method, or class definition, and which describes the purpose of that object) should be included for all classes, for any function of significant complexity, and for any method other than special methods (whose names begin and end with __).

  • CS3 - Informative names: Names of functions, variables, and classes should be descriptive and not abbreviated to the point of incomprehensibility. For example, single-letter names should be avoided except for variables that have a very short scope and obvious purpose (e.g. an iterator in a short loop or a comprehension expression)

  • CS4 - Indenting: Use at least two spaces to indent a block of code relative to its parent. Use that same number of spaces for all indented blocks.

  • CS5 - 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.

  • CS6 - Avoid manual indexing: Any time you need perform some operation for every element of a list or other data structure, use Python's iterator protocol (e.g. for x in L:, followed by use of x) rather than manual indexing (e.g. for i in range(len(L)):, followed by use of L[i]). If you believe range(len(...))) and manual indexing is the natural way to approach a task you are given in this course, ask the instructor or TA for confirmation.

Examples and discussion

The list of requirements from the previous section is duplicated here, with additional explanation and examples.

CS1 - Header

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

  • Line 1: The course and assignment (e.g. MCS 275 Spring 2024 Homework 5)
  • Line 2: Your full name
  • Line 3: A statement accurately describing the authorship and/or sources of the work (which under the course rules should either be that you wrote it from scratch, or that we provided some template and that you are the sole author of the changes made to that 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 2024 Homework 6
# 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 2024 Homework 6
# 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 - Comments and docstrings

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 clarify why and how things are done. Docstrings (meaning a string literal that appears as the first statement inside a function, module, method, or class definition, and which describes the purpose of that object) should be included for all classes, for any function of significant complexity, and for any method other than special methods (whose names begin and end with __).

Why we have this rule

Documentation is extremely important in computer programming. Comments are one kind of documentation, and provide an important aid to reading code. Being able to explain your work and ideas in written form is broadly important throughout computer science (indeed all of science).

Python also 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 class definition lacks docstrings for the class and its methods. (Note: This example isn't fully self-contained as it makes reference to another class called Vector2. See the full source code for this example module for a self-contained version.)

In [ ]:
class Point2:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        if isinstance(other, Point2):
            return (self.x == other.x) and (self.y == other.y)
        else:
            return False

    def __add__(self, other):
        if isinstance(other, Vector2):
            # we have been asked to add this Point2 to another Vector2
            return Point2(self.x + other.x, self.y + other.y)
        else:
            # we have been asked to add this Point2 to some other object
            # we don't want to allow this
            return NotImplemented  # return this to forbid the requested operation

    def __sub__(self, other):
        if isinstance(other, Point2):
            #                 Q.x - P.x        Q.y - P.y
            return Vector2(self.x - other.x, self.y - other.y)
        else:
            return NotImplemented

    def __str__(self):
        return "Point2({},{})".format(self.x, self.y)

    def __repr__(self):
        return str(self) # use the same string for interactive mode as with print()

    def distance_to(self, other):
        if isinstance(other, Point2):
            return abs(self - other)
        else:
            raise TypeError("distance_to requires argument of type Point2")

Compliant example

This modified version is acceptable. It includes docstrings for some of the special methods as well, which is appropriate in this case but not required by this rule.

In [ ]:
class Point2:
    "Point in the plane"

    def __init__(self, x, y):
        "Initialize new point from x and y coordinates"     
        self.x = x
        self.y = y

    def __eq__(self, other):
        "points are equal if and only if they have same coordinates"
        if isinstance(other, Point2):
            return (self.x == other.x) and (self.y == other.y)
        else:
            return False

    def __add__(self, other):
        "point+vector addition"
        if isinstance(other, Vector2):
            # we have been asked to add this Point2 to another Vector2
            return Point2(self.x + other.x, self.y + other.y)
        else:
            # we have been asked to add this Point2 to some other object
            # we don't want to allow this
            return NotImplemented  # return this to forbid the requested operation

    def __sub__(self, other):
        "point-point subtraction, gives displacement vector"
        if isinstance(other, Point2):
            #                 Q.x - P.x        Q.y - P.y
            return Vector2(self.x - other.x, self.y - other.y)
        else:
            return NotImplemented

    def __str__(self):
        return "Point2({},{})".format(self.x, self.y)

    def __repr__(self):
        return str(self) # use the same string for interactive mode as with print()

    def distance_to(self, other):
        "get distance between two points"
        if isinstance(other, Point2):
            return abs(self - other)
        else:
            raise TypeError("distance_to requires argument of type Point2")

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)

CS3 - Informative names

Names of functions, variables, and classes should be descriptive and not abbreviated to the point of incomprehensibility. For example, single-letter names should be avoided except for variables that have a very short scope and obvious purpose (e.g. an iterator in a short loop or a comprehension expression)

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) ]

CS4 - Indenting

Use at least two spaces to indent a block of code relative to its parent. Use that same number of spaces for all indented blocks.

Why we have this rule

Inconsistently indented code, or code that uses only one space to indent blocks, is difficult to read. Also, some variations in indenting constitute 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.")

CS5 - 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.

CS6 - Avoid manual indexing

Any time you need perform some operation for every element of a list or other data structure, use Python's iterator protocol (e.g. for x in L:, followed by use of x) rather than manual indexing (e.g. for i in range(len(L)):, followed by use of L[i]). > If you believe range(len(...))) and manual indexing is the natural way to approach a task you are given in this course, ask the instructor or TA for confirmation.

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, easier to read, and less prone to bugs related to incorrect index math.

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

  • 2024-01-04 Initial publication
  • 2024-05-14 Update Dumas name