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.
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:
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.
The list of requirements from Section 2 is duplicated here, with a rationale for each rule and at least one code example.
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.)
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.
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.
# 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.
"""
For an assignment where the course staff provide some kind of template code that students are expected to modify or add to.
# 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.
"""
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).
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).
This function lacks a docstring.
def prefix(s):
# <-- PROBLEM HERE
return s[:3]
This modified version is acceptable.
def prefix(s):
"""Return the first three characters of a string ‘s‘"""
return s[:3]
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.
# 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)
This modified version is acceptable.
# 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)
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.
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.
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.)
for item in container:
"""Display the weight of this item (units will be added by the item.weight object)""" # <-- PROBLEM HERE
print(item.weight)
This modified version is acceptable.
for item in container:
# Display the weight of this item (units will be added by the item.weight object)
print(item.weight)
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.
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.
This code defines two variables (presumably for later use) whose names give no clear indication of their purpose or contents.
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
This modified version is acceptable.
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 ]
This modified version is also acceptable, and uses list comprehensions to make the code shorter and more readable.
shifted_range = [ i-3 for i in range(8) ]
squares_plus_one = [ (i+1)*(i+1)+1 for i in range(8) ]
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).
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.
This code example uses various indent lengths, including one below 2 spaces.
# 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
Make all indent lengths 4 spaces.
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.")
Don’t use
str()
on something that is already a string, don't useint()
on something that is already an integer, don’t convert a string to a list just to index it (strings already support indexing!), etc.
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.
The return value of input()
is already a string, so there's no need to convert it.
s = str(input("Enter a line of text: "))
s = input("Enter a line of text: ")
Strings support indexing, so there is no need to convert to a list just to get a character by index.
s = "Python strings support indexing!"
L = list(s) # <-- PROBLEM HERE
print(L[5],L[11])
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.
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.
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.
Using range(len(...))
to generate integer indices just so we can visit each element of a list using a for
loop.
for i in range(len(users)): #<-- PROBLEM HERE
print("Username:",users[i])
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.
for user in users:
print("Username:",user)
for i in range(len(users)): #<-- PROBLEM HERE
print("User",i,"has username",users[i])
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.
for i,username in enumerate(users):
print("User",i,"has username",username)
These are suggestions, not requirements, but they encourage some good habits and code style.
"
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.)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.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.