Variables are used effectively and help to avoid repetition

Variables serve many purposes in your code, but one is to avoid repeating yourself and to make your code more readable. For example, imagine you are asked to write a function that computes the area and the circumference of a circle:

def area_and_circumference(radius):
    """
    Computes the area and circumference of a circle

    Arguments:
        radius (float): the radius of the circle
    Returns:
        Tuple[float, float]: the area of the circle and the circumference of the circle
    """
    area = 3.14 * radius ** 2
    circumference = 2 * 3.14 * radius
    return area, circumference

You’ll see that I write out 3.14 manually twice in the code. Imagine I want to make my calculation more precise, and replace 3.14 with 3.14159265358979323846. I’ll have to update my code in two places! Here’s a better solution:

def area_and_circumference(radius):
    """
    Computes the area and circumference of a circle

    Arguments:
        radius (float): the radius of the circle
    Returns:
        Tuple[float, float]: the area of the circle and the circumference of the circle
    """
    pi = 3.14
    area = pi * radius ** 2
    circumference = 2 * pi * radius
    return area, circumference

Now, it will be easier to change 3.14 to 3.14159265358979323846, and as an added bonus, the code is more readable.

All branches of code are reachable

When you write code, you should make sure that it is possible for each line of code to run given some input to the function. Consider the following code:

def do_modulo(x):
    """
    A silly function that returns a value based on x % 2

    Args:
        x (int): A number to compute the modulo of
    Returns:
        int: the result of our silly computations
    """
    y = 10
    if x % 2 == 1:
        y += 3
    elif x % 2 == 3:
        y -= 3
    else:
        y += 1
    return y

It is impossible for the condition x % 2 == 3 to evaluate to True, so the condition should not be included in the code!

Brevity: you should not copy-paste code within your program if at all possible!

Imagine you are asked to write a program that includes functions for computing the areas of rectangles and squares. You might write code that looks like this:

def compute_rectangle_area(length_1, length_2):
    """
    Computes the area of a rectangle

    Arguments:
        length_1 (float): the length of one side
        length_2 (float): the length of the other side
    Returns:
        float: the area of the rectangle
    """
    return length_1 * length_2

def compute_square_area(side_length):
    """
    Computes the area of a square

    Arguments:
        side_length (float): the length of a side of the square
    Returns:
        float: the area of the square
    """
    return side_length * side_length

However (especially as your code begins to get more complex), it would be better to recognize that these functions are essentially doing the same thing. Instead, you can write compute_square_area as

def compute_square_area(side_length):
    """
    Computes the area of a square

    Arguments:
        side_length (float): the length of a side of the square
    Returns:
        float: the area of the square
    """
    return compute_rectangle_area(side_length, side_length)

Avoiding copy-paste is especially relevant when you are trying to do the exact same thing multiple times. Imagine you are asked to square all numbers from 1 to 10 and add them up. You could write this code:

def square_and_sum():
    """
    Squares all numbers from 1-10 and computes the sum

    Arguments:
        N/A
    Returns:
        float: the sum of the squares
    """
    first = 1 ** 2
    second = 2 ** 2
    third = 3 ** 2
    fourth = 4 ** 2
    fifth = 5 ** 2
    sixth = 6 ** 2
    seventh = 7 ** 2
    eigth = 8 ** 2
    ninth = 9 ** 2
    tenth = 10 ** 2

    return first + second + third + fourth + fifth + seventh + eigth + eight + ninth + tenth

You might notice that there is a bug in this code! While it attempts to solve the problem, the fact that it doesn’t use loops (a) makes it less readable and less generalizable and (b) makes it more prone to human error. Here’s a better way to write it:

def square_and_sum():
    """
    Squares all numbers from 1-10 and computes the sum

    Arguments:
        N/A
    Returns:
        float: the sum of the squares
    """
    total = 0
    for i in range(1, 11):
        total += i ** 2
    return total

Now that we’ve written this code in a more generalizable way, it would be much easier to change the funcition to one that squares all numbers from start to end (inclusive) and computes the sum:

def square_and_sum(start, end):
    """
    Squares all numbers from start-end (inclusive) and computes the sum

    Arguments:
        start (int): the first number in the sequence
        end (int): the last number in the sequence
    Returns:
        float: the sum of the squares
    """
    total = 0
    for i in range(start, end + 1):
        total += i ** 2
    return total

Essentially, whenever you are doing something more than two times, please use a loop!

Code shows good understanding of programming constructs

Make sure that the code you write demonstrates your understanding of programming constructs. Imagine you turn in code that includes the following function, which is meant to return a random number from a list:

import random

def choose_random(numbers):
    """
    Chooses a random number from a list

    Arguments:
        numbers (List[int]): a list of numbers
    Returns:
        int: one random number from the list
    """
    for number in numbers:
        random_number = random.choice(numbers)
        return random_number

In this code, the for loop essentially serves no purpose1; the variable number is never used, and we return in the first iteration of the loop, rendering the loop meaningless. The code should probably be written as follows (with the assumption that the list is not empty):

import random

def choose_random(numbers):
    """
    Chooses a random number from a list

    Arguments:
        numbers (List[int]): a list of numbers
    Returns:
        int: one random number from the list
    """
    random_number = random.choice(numbers)
    return random_number

The fact that a loop was used shows a lack of understanding of how loops work in conjunction with return statements.

Appropriate use of data structures

Before you use a list or a dictionary, make sure to consider which is more appropriate for your use case. For example, imagine you are writing code for a contacts app for your phone. If contacts is a list of tuples, your code might look like this:

def get_phone_number(contacts, name):
    """
    Gets the phone number of a contact from the contacts list

    Arguments:
        contacts (List[Tuple[str, str]]): a list of contacts; 
                 the first element is the name and the second is the number
        name: The name of a contact
    Returns:
        string: the phone number of the contact (None if the contact is not in the list)
    """
    for contact in contacts:
        contact_name, contact_number = contact
        if contact_name == name:
            return contact_number
    return None

But if you use a dictionary instead, you could write the code more efficiently:

def get_phone_number(contacts, name):
    """
    Gets the phone number of a contact from the contacts dictionary

    Arguments:
        contacts (Dict[str, str]): a dictionary of contacts; 
                 the keys are names and the values are phone numbers
        name: The name of a contact
    Returns:
        string: the phone number of the contact (None if the contact is not in the dictionary)
    """
    if name in contacts:
        return contacts[name]
    return None
  1. It does lead to us returning None if the list is empty, rather than the code raising an IndexError