Python Code Quality

Variables

Local variable not in snake_case

Local variables (meaning variables inside functions) should always be written using snake_case (i.e., they should be written using all lower case letters, with words being separated by underscores).

WRONG code:

1
2
def add_numbers(nums):
    sumTotal = 0

Fixed code:

1
2
def add_numbers(nums):
    sum_total = 0

Global variable not in SHOUT_CASE

Global variables (meaning variables outside functions) should always be written using SHOUT_CASE (i.e., they should be written using all upper case letters, with words being separated by underscores).

WRONG code:

1
2
3
errorMessage = "Runtime Error"
def print_error():
    print(errorMessage)

Fixed code:

1
2
3
ERROR_MESSAGE = "Runtime Error"
def print_error():
    print(ERROR_MESSAGE)

Multiple assignment

Assigning multiple variables at once can be confusing; instead, you should assign the variables in multiple lines to indicate where each value is coming fromm.

WRONG code:

1
a = b = v

WRONG code:

1
a, b = v

Fixed code:

1
2
a = v
b = v

Variable assigned to itself

Variables should not be assigned to themselves, because it does not change the functionality of the code at all.

WRONG code:

1
sum_total = sum_total

Fixed code:

1

Variable named after a built-in

Built-in python functionality refers to any function defined by the language itself, like how list() is an existing function. Using built-ins as variable names should be avoided, because it can make code incredibly confusing to read.

WRONG code:

1
list = ["a", "b", "c"]

Fixed code:

1
str_list = ["a", "b", "c"]

Magic constant

A magic constant is any constant used within the scope of a function definition, excluding the set {0, -1, 1, 2, "", " ", inf, -inf, list(), tuple(), set(), dict()}. These are excluded because they are used incredibly frequently. Instead of using magic constants, if you want to reference a constant other than these, you should store it in a global variable. This way, it’s easy to tell where all your variables are stored, and it’s easy to change them quickly.

WRONG code:

1
2
def circle_area(r):
    return 3.14159 * r * r

Fixed code:

1
2
3
PI = 3.14159
def circle_area(r):
    return PI * r * r

Dead Store

A dead store refers to when you store information in a variable, but then never access the variable again for whatever reason, whether that’s for comparison, printing, returning, etc.

WRONG code:

1
2
3
4
5
6
def is_sum_odd(nums):
    is_odd = False
    sum_total = 0
    for num in nums:
        sum_total += num
    return sum_total % 2

Fixed code:

1
2
3
4
5
def is_sum_odd(nums):
    sum_total = 0
    for num in nums:
        sum_total += num
    return sum_total % 2

Variable type mismatch

Variables should only ever take on one type of data (i.e., integer, string, boolean, etc.). Using the same variable with multiple types of values creates very confusing code.

WRONG code:

1
2
variable = 10
variable = True

Fixed code:

1
2
variable = 10
variable_2 = True

Operators

Improper operator spacing

Binary operators (meaning anything we use to modify variables, like addition, subtraction, equals, etc) should always have one space between them and the variables next to them.

WRONG code:

1
sum_total+=num

Fixed code:

1
sum_total += num

Unclear use of boolean operator

When you want to check if an element a is in one of two iterables b, and c, you should write a in b or a in c, not a in b or c, as the latter will evaluate c as a boolean condition.

WRONG code:

1
2
def in_two_lists(a, b, c):
    return a in b or c

Fixed code:

1
2
def in_two_lists(a, b, c):
    return a in b or a in c

Oversimplified DeMorgan’s Law

DeMorgan’s law is defined as the two statements: not (a or b) = not a and not b and not (a and b) = not a or not b. In general, it’s better practice to limit the number of boolean operators that appear; so, we should always use the left side of the above equations when given the opportunity.

WRONG code:

1
return not a and not b

Fixed code:

1
return not (a or b)

Simplifiable not

When using a comparator like == or is, you should always use the not with the comparator, instead of putting it in front.

WRONG code:

1
return not a == b

Fixed code:

1
return a != b

WRONG code:

1
return not a is None

Fixed code:

1
return a is not None

Using ‘is’ instead of ‘==’

Except for comparing to None, You should always be using ‘==’ to compare equality, not ‘is’.

WRONG code:

1
return a is b

Fixed code:

1
return a == b

Function Definitions

Function definition not in snake_case

Function definitions should always be written using snake_case (i.e., they should be written using all lower case letters with words being separated by underscores).

WRONG code:

1
def AddNumbers(nums):

Fixed code:

1
def add_numbers(nums):

If statements

Boolean comparison

Whenever you have a variable that stores the boolean True or False, the variable itself can be used in conditionals. It is redundant to check if the boolean variable itself is either True or False by using ==.

WRONG code:

1
2
if condition == True:
    do something

Fixed code:

1
2
if condition:
    do something

Boolean zen

Boolean zen refers to the efficient use of booleans. Whenever you write an if [condition] statement, the condition itself returns a boolean value when the code is run. If you want to simply return the value of the condition, you should return the condition instead of checking the condition.

WRONG code:

1
2
3
4
if first < second:
    return True
else:
    return False

Fixed code:

1
return first < second

Unnecessary if/else

Any if statement that takes the form of if [condition]: pass does nothing, and should be removed. Similarly, any else that takes the form of else: pass does nothing, and should be removed.

WRONG code:

1
2
3
if first < second:
    pass
return first < second

Fixed code:

1
return first < second

Extra parentheses

In python, if and while statements should not be surrounded by parentheses.

WRONG code:

1
if (first < second):

Fixed code:

1
if first < second:

Negate if statement

It’s bad practice to write an empty if statement, but a full else statement. To save room and make your code look cleaner, you should add not to your original if statement instead, and move the code in the body of the else case into the body of the if statement.

WRONG code:

1
2
3
4
if a:
    pass
else:
    print("False")

Fixed code:

1
2
if not a:
    print("False")

Nested else-if statement

When nested if statements do not have elif clauses, elif statements should be used instead.

WRONG code:

1
2
3
4
5
6
7
if first < second:
    print("first is less than second")
else:
    if second < first:
        print("second is less than first")
    else:
        print("first is equal to second")

Fixed code:

1
2
3
4
5
6
if first < second:
    print("first is less than second")
elif second < first:
    print("second is less than first")
else:
    print("first is equal to second")

Nested if statements

Nested if statements should be simplified using boolean operators.

WRONG code:

1
2
3
4
MESSAGE = "a and b"
if a:
    if b:
        print(MESSAGE)

Fixed code:

1
2
3
MESSAGE = "a and b"
if a and b:
    print(MESSAGE)

Loops

Modified an iterator variable

Since loop variables are updated with each iteration of the loop, they shouldn’t be modified.

WRONG code:

1
2
3
for i in range(n)
    i += 1
    print(i)

Fixed code:

1
2
for i in range(n)
    print(i + 1)

For loop hygiene

If a for loop iterates over indices of a list, but only uses those indices to access the element at that index, the for loop should be re-written to iterate solely over those elements instead.

WRONG code:

1
2
for i in range(len(ls))
    print(ls[i])

Fixed code:

1
2
for elem in ls
    print(elem)

While loop hygiene

If a while loop is written so that it uses a counter and continues to run until that counter reaches a certain value, a for loop should be used instead.

WRONG code:

1
2
3
4
i = 0
while i < len(ls) - 1
    print(ls[i + 1] - ls[i])
    i += 1

Fixed code:

1
2
for i in range(len(ls) - 1)
    print(ls[i + 1] - ls[i])

Repeated code

Duplicated code inside if

Duplicated code that occurs either at the start or end of an if statement should be moved outside the if statement or the cases should be collapsed.

WRONG code:

1
2
3
4
5
6
7
8
9
if a % b == 0:
    print("b divides a")
    print("done")
elif b % a == 0:
    print("a divides b")
    print("done")
else:
    print("failed to divide")
    print("done")

Fixed code:

1
2
3
4
5
6
7
if a % b == 0:
    print("b divides a")
elif b % a == 0:
    print("a divides b")
else:
    print("failed to divide")
print("done")

Duplicated code

Duplicate code should not occur in your program. This includes code where the only difference between the two blocks is constant values.

WRONG code:

1
2
3
4
5
6
total = 0
for i in range(n):
    total += i + 1
for i in range(n):
    total += i + 2
print(total)

Fixed code:

1
2
3
4
5
6
7
def sum_plus_d(d):
    total = 0
    for i in range(n):
        total += i + d
    return total
total = sum_plus_d(1) + sum_plus_d(2)
print(total)

Repeated return

If you use the same return in multiple parts of an if statement, the returns should be collapsed

WRONG code:

1
2
3
4
5
6
7
def is_multiple_of_2_or_3(n):
    if n % 2 == 0:
        return True
    elif n % 3 == 0:
        return True
    else:
        return False

Fixed code:

1
2
3
4
5
def is_multiple_of_2_or_3(n):
    if n % 2 == 0 or n % 3 == 0:
        return True
    else:
        return False

Repeated function call

Identical function calls should not be repeated. This causes the entire function to be rerun, which adds excessive steps to the program.

WRONG code:

1
2
3
4
5
def do_something(n):
    if calculate(n):
        return calculate(n)
    else:
        return None

Fixed code:

1
2
3
4
5
6
def do_something(n):
    value = calculate(n)
    if value:
        return value
    else:
        return None

Miscellaneous

Improper Dict Use

When checking if an element is a key of a dictionary or iterating over a dictionary, we can just use x in dict, we don’t need to directly check if x is in the set of keys, dict.keys().

WRONG code:

1
2
3
def print_entries(d: dict[str, str]):
    for key in d.keys():
        print(key + ": " + d[key])

Fixed code:

1
2
3
def print_entries(d: dict[str, str]):
    for key in d:
        print(key + ": " + d[key])

Lack of helper functions

A code block with more than 5 consecutive layers of indentation can be reorganized to be easier to read. This reoganization can be done either by simplifying the code or rewriting it using a helper function. The example reduced the number of indents by using a helper function.

WRONG code:

1
2
3
4
5
6
def main():
# variable declarations omitted for clarity
    if condition_1:
        for i in list_2:
            if condition_3:
                print(i)

Fixed code:

1
2
3
4
5
6
7
8
9
def main():
# variable declarations omitted for clarity
    if condition 1:
        do_thing(list_2, condition_3)

def do_thing(list_2: list[int], condition_3: bool):
    for i in list_2:
        if condition_3:
            print(i)

Missing Docstring

Every function should have a non-empty docstring (docstrings were excluded in previous examples to save space). Docstrings should achieve three goals: explain the purpose of the function, explain the type and meaning of each argument, and explain the type and meaning of what the function returns. Learning to write docstrings is an important skill, because other programmers (and future-you) will need to understand your code!

WRONG code:

1
2
def round_down(n):
    return (n / 2) * 2

Fixed code:

1
2
3
4
5
6
7
8
9
10
11
def round_down(n):
    """
    Returns `n` rounded down to the nearest integer

    Args: n (int): the number to round.

    Returns:
        int: `n` rounded down to the nearest integer
    """

    return (n / 2) * 2

Commented-out Code

When finished with a project, all commented-out code should be removed. Leaving it in unnecessarily clutters your code and can confuse other readers. However, sometimes comments in plain English are helpful if they explain a complicated piece of code that can’t be easily understood from the code alone.

WRONG code:

1
2
3
4
def print_values(d: dict[str, str]):
    # for key in d:
    #     print(d[key])
    print(d.values())

WRONG code:

1
2
3
def print_values(d: dict[str, str]):
    # prints the value set of the dict
    print(d.values())

Fixed code:

1
2
def print_values(d: dict[str, str]):
    print(d.values())

Dead Code

Dead code refers to code that is never executed (e.g., code that is written after a return). Dead code should always be deleted before submission.

WRONG code:

1
2
3
return first < second
if first < second:
    print("first is less than second")

Fixed code:

1
2
3
if first < second:
    print("first is less than second")
    return True

Ellipsis

Ellipses should not remain in your code after a finished assignment. In CS1 we will use ellipses to show where you are supposed to write code in an assignment, but they should be deleted afterwards.

WRONG code:

1
2
def to_do(n):
    ...

Fixed code:

1
2
def to_do(n):
    # implementation

Empty return

Since empty returns return nothing, they should either be replaced with return None or deleted depending on the use case.

WRONG code:

1
return

Fixed code:

1