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
It does lead to us returning
None
if the list is empty, rather than the code raising anIndexError
↩