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 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:
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.
The list of requirements from the previous section is duplicated here, with additional explanation and examples.
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).
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 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.
"""
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 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.
"""
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__
).
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).
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.)
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")
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.
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")
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. 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)
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) ]
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.
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.
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.
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 ofx
) rather than manual indexing (e.g.for i in range(len(L)):
, followed by use ofL[i]
). > If you believerange(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.
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.
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.