Common Deductions
This page outlines some of the common kinds of deductions graders are looking for in reviewing your code.
This is not an exhaustive list, other penalties may be applied for assignment-specific restrictions, solutions that bypass the point of the assignment, etc.
These will often be noted in the assignment write-up, but it is impossible to enumerate every potential deduction.
Code Quality
Code is read much more often than it is written.
Producing code that solves a problem is only part of the task, that code needs to be intelligible to your fellow developer and/or future self. Hard to understand & overly complex code is source of subtle bugs.
Each assignment will be evaluated for the presence of common code quality issues.
CQ: Banned Constructs (-2)
Some Python features are considered bad practice in production code.
While you will see these in examples, most projects & teams prohibit their use.
Banned Constructs:
- use of a bare
exceptorexcept Exceptionstatement - use of
import *is explicitly banned in this class
Examples:
from modA import *
from modB import *
# The origin of symbols used in this file are not clear.
# In fact, reordering the imports or changes to either
# could result in the code breaking if the two modules
# define the same.# INCORRECT: this code incorrectly catches the NameError
try:
func(this_variabl_has_typos)
except:
...
# CORRECT: this only catches ValueError, so if func raises
# that error it is correctly handled
try:
func(this_variabl_has_typos)
except ValueError:
...CQ: Incomplete (-1)
An assignment that is missing significant portions will have this penalty applied in addition to any present in the code.
(This makes it less likely that incomplete assignments will get an S due to lack of code meaning a lack of style issues.)
CQ: Function Decomposition (-1)
- The presence of copy/pasted code where it would be more appropriate to use a helper function.
- One large function that does many things instead of smaller functions.
CQ: Non-idiomatic (Unpythonic) Code (-1)
Code that does not use Python’s features correctly.
Example:
# INCORRECT: this code obtains an integer only to index back into
for i in range(len(items)):
print(items[i])
# CORRECT: use direct iteration, or enumerate
for item in items:
print(item)
for idx, item in enumerate(items):
print(idx, item)CQ: Hard to Read (-0.5)
Not following other general formatting guidelines that aim to increase readability.
- Improper indentation.
- Code consistently exceeding line length.
- Improper use of blank lines per style guide.
- Debugging statements left in (
print,breakpoint, etc.). - Use of “magic numbers” or strings where constants would be appropriate.
CQ: Variable Names (-0.5)
- Variable names should be meaningful & aid in understanding of the code.
- Avoid using single letter names (other than in common contexts like i/j to loop over integers).
CQ: Commenting (-0.5)
Comments should enhance understanding of the code.
- Code that is significantly under-commented. (Complex sections with no explanation at all.)
- Code that is significantly over-commented, meaning code that describes “how” instead of “why”.
- Missing docstrings on multiple functions.
Efficiency
EF: Duplicated Data (-1)
Duplicating data in unnecessary ways, such as copying a list to a tuple for no reason.
# INCORRECT: creates three copies of the list of words
def get_unique_words(text):
words = text.split()
lowercase_words = [word.lower() for word in words] # copy 1
cleaned_words = [word.strip('.,!?') for word in lowercase_words] # copy 2
unique_words = set(cleaned_words) # copy 3
return unique_words
# CORRECT: only a single copy
def get_unique_words_efficient(text):
unique_words = set()
for word in text.split():
unique_words.add(word.lower().strip('.,!?'))
return unique_wordsOr a more signficant example:
# CORRECT
for item in csv.DictReader(fd):
process_data(item)
# INCORRECT: creates an entire second copy of the data
# despite there only being a need to access one at a time
items = list(csv.DictReader(fd))
for item in items:
process_data(item)EF: Redundant Work (-1)
Doing redundant work, like going through a list twice when a single pass would have sufficed.
This can be thought of as “too many loops”, looping three or more times when one would do.
Or in terms of complexity, this can be thought of as turning an O(N) into an O(2N) or similar.
Example:
# INCORRECT: this code iterates through the data twice
count_positive = sum(1 for x in data if x > 0)
count_negative = sum(1 for x in data if x < 0)
# CORRECT: this code iterates once
count_negative = 0
count_positive = 0
for x in data:
if x < 0:
count_negative += 1
elif x > 0:
count_positive += 1EF: Major Complexity Error (-2)
This penalty applies to solutions where a drastically more efficient solution exists.
This can be because of unnecessary looping/recursion or an incorrect data structure (e.g. searching a list for items when a dictionary would have given O(1) lookup)
In terms of complexity this might mean:
- O(1) into O(N)
- O(N) into O(N^2)
Example:
# INCORRECT: O(N^2) implementation that needs a nested for loop
def find_duplicates_quadratic(lst):
duplicates = []
for i in range(len(lst)):
for j in range(i + 1, len(lst)):
if lst[i] == lst[j] and lst[i] not in duplicates:
duplicates.append(lst[i])
return duplicates
# CORRECT: O(N) implementation that uses a set appropriately
def find_duplicates_linear(lst):
seen = set()
duplicates = set()
for item in lst:
if item in seen:
duplicates.add(item)
seen.add(item)
return duplicatesEF: Reimplemented built-ins (-0.5)
It is almost always more efficient to use the built-in functions in the language over implementing logic yourself.
Common examples:
- iterating through a string/list to find a single element instead of
container.find(item)or similar - Combining lots of strings using
+instead of.joinor f-strings.
EF: Other Minor Efficiency Issue (-0.5)
Small issues that lead to code being slower/less efficient than it should be.
Common examples:
- Incorrect data structure (e.g. using a list to keep unique items instead of a set)
- Repeatedly calling a function inside a loop with the same arguments, should be moved outside the loop.