Skip to content

Clean Code Example in Python

Amin Zamani edited this page Dec 21, 2023 · 9 revisions

1. Use meaningful and descriptive names for variables, functions, and classes

# Bad example
a = 5
b = 10

def c(x, y):
    return x + y

# Good example
width = 5
height = 10

def calculate_area(width, height):
    return width * height

In the bad example, the variable names a and b are not very descriptive, and the function name c doesn't provide any information about what it does.

In the good example, the variable names width and height clearly describe what they represent, and the function name calculate_area tells us exactly what the function does.

2. Keep functions and classes small and focused on a single responsibility

Write functions that do just one thing

# bad example
def process_data(data):
    # do lots of processing
    # and output lots of results
    # in one big function

# good example
def preprocess_data(data):
    # do some preprocessing

def calculate_results(data):
    # calculate some results

def output_results(results):
    # output the results

3. Use comments to explain why code is doing something, not what it is doing

# bad example
def calc(a, b):
    # multiply a by b
    return a * b

# good example
def calc_salary(hours_worked, hourly_rate):
    # calculate the salary for an employee
    return hours_worked * hourly_rate

4. Use consistent formatting and indentation throughout your code

def calculate_area(radius):
    # consistent indentation with 4 spaces
    pi = 3.14159
    area = pi * radius ** 2  # consistent use of spaces around operators
    return area

In the above example, the code uses consistent indentation with 4 spaces, which is the recommended standard for Python. Additionally, the code uses consistent use of spaces around operators for readability

5. Avoid long and complex conditional statements

# an example of a long and complex conditional statement:
if x > 0 and y < 10 or (z == 5 and w != 2) and (a == True or b == False):
    # do something

# To make this code cleaner and easier to read, we can break up the conditional into smaller parts with descriptive variable names
is_positive = x > 0
is_y_small = y < 10
is_z_five = z == 5
is_w_not_two = w != 2
is_a_true = a == True
is_b_false = b == False

if (is_positive and is_y_small) or (is_z_five and is_w_not_two) and (is_a_true or is_b_false):
    # do something

By breaking up the conditional into smaller parts and using descriptive variable names, we can make the code more readable and easier to understand

6. Avoid deep nesting of code blocks

def calculate_salary(employee):
    if employee.is_full_time:
        salary = calculate_full_time_salary(employee)
    else:
        if employee.is_contractor:
            salary = calculate_contractor_salary(employee)
        else:
            if employee.is_intern:
                salary = calculate_intern_salary(employee)
            else:
                salary = 0 # no salary if employee type not recognized
    return salary

# This code has three levels of nesting, which can be hard to read and maintain.
# Here's a refactored version that avoids deep nesting:

def calculate_salary(employee):
    salary = 0
    if employee.is_full_time:
        salary = calculate_full_time_salary(employee)
    elif employee.is_contractor:
        salary = calculate_contractor_salary(employee)
    elif employee.is_intern:
        salary = calculate_intern_salary(employee)
    return salary

# This code uses elif statements to eliminate deep nesting, making it easier to read and maintain.

7. Use descriptive and well-structured error messages and exception handling.

try:
    num1 = int(input("Enter the first number: "))
    num2 = int(input("Enter the second number: "))
    result = num1 / num2
    print("The result is:", result)
except ValueError as e:
    print("Please enter valid integers.")
except ZeroDivisionError as e:
    print("Cannot divide by zero.")
except Exception as e:
    print("An error occurred:", str(e))

8. Use consistent and clear naming conventions for classes, functions, and variables

class Car:
    def __init__(self, make, model, year, color):
        self.make = make
        self.model = model
        self.year = year
        self.color = color
        
    def start_engine(self):
        print("Engine started")
        
    def stop_engine(self):
        print("Engine stopped")
        
    def drive(self):
        print("Driving")

In this example, we use the UpperCamelCase convention for the class name Car, and lowercase words separated by underscores for the function names and variable names. This makes the code more readable and consistent.

9. Write modular and reusable code by separating concerns and avoiding tight coupling

# example.py

# define a function that reads data from a file
def read_data(file_path):
    with open(file_path, 'r') as file:
        data = file.read()
    return data

# define a function that processes the data
def process_data(data):
    processed_data = data.upper()
    return processed_data

# define a function that writes data to a file
def write_data(file_path, data):
    with open(file_path, 'w') as file:
        file.write(data)

# define the main function that ties everything together
def main(file_path):
    data = read_data(file_path)
    processed_data = process_data(data)
    write_data(file_path, processed_data)

# call the main function
if __name__ == '__main__':
    main('example.txt')

In this example, we have separated the concerns of reading, processing, and writing data into three separate functions. Each function is responsible for a specific task, and none of them depend on each other. This allows us to reuse these functions in other parts of our code without having to worry about how they interact with each other.

The main function ties everything together by calling the read_data, process_data, and write_data functions in the appropriate order. This allows us to easily modify the behavior of the program by changing the implementation of these functions without having to modify the main logic of the program.

10. Avoid using magic numbers or hard-coded values in your code

In programming, "magic numbers" refer to hardcoded numeric values used in code without any explanation or context. These values are often used without being assigned to a named variable or constant, which makes the code difficult to understand and maintain.

For example, in the code if x > 5 and x < 10:, the numbers 5 and 10 are magic numbers because they are used without any explanation or context. A better approach would be to define them as constants or variables with descriptive names, like LOWER_BOUND = 5 and UPPER_BOUND = 10.

# Bad code with magic numbers
def calculate_circle_area(radius):
    # pi value as a magic number
    return 3.14159 * radius * radius


# Good code without magic numbers
import math

def calculate_circle_area(radius):
    return math.pi * radius ** 2

# Or
def calculate_circle_area(radius):
    PI = 3.14159
    return PI * radius ** 2

In the bad code example, the value of pi is hardcoded as a magic number within the function, which can make it difficult to maintain and update in the future.

In the improved code, we define a constant variable named PI outside the function and use it within the function, making it much easier to read, maintain and update the value of PI if necessary.

11. Use version control to keep track of changes and collaborate with other developers

By using version control, you can easily keep track of changes, collaborate with other developers, and revert to previous versions of your code if necessary.

12. Write tests to ensure that your code is correct and behaves as expected

import math

def calculate_area(radius):
    if radius <= 0:
        raise ValueError("Radius must be positive")
    area = math.pi * (radius ** 2)
    return area

def test_calculate_area():
    assert calculate_area(0) == 0
    assert calculate_area(1) == math.pi
    assert calculate_area(2.5) == math.pi * (2.5 ** 2)
    try:
        calculate_area(-1)
    except ValueError as e:
        assert str(e) == "Radius must be positive"

In this example, the calculate_area() function takes in a radius as an argument, checks that it is a positive value, calculates the area of the circle using the math.pi constant and returns the result. The test_calculate_area() function tests the output of the calculate_area() function for several different input values and ensures that it returns the expected output.

By writing tests for our code, we can catch bugs and errors early on and ensure that our code behaves correctly and consistently.

13. Refactor your code regularly to improve its readability and maintainability

# Before refactoring:
def calculate_price(product, quantity):
    discount_rate = 0.1 if quantity > 10 else 0.05
    price = product.price * quantity
    if product.category == "electronics":
        tax_rate = 0.15
    elif product.category == "clothing":
        tax_rate = 0.08
    else:
        tax_rate = 0.1
    total_price = (price - (price * discount_rate)) * (1 + tax_rate)
    return total_price

# After refactoring:
def calculate_price(product, quantity):
    discount_rate = get_discount_rate(quantity)
    tax_rate = get_tax_rate(product.category)
    price = product.price * quantity
    total_price = (price - (price * discount_rate)) * (1 + tax_rate)
    return total_price

def get_discount_rate(quantity):
    if quantity > 10:
        return 0.1
    else:
        return 0.05

def get_tax_rate(category):
    if category == "electronics":
        return 0.15
    elif category == "clothing":
        return 0.08
    else:
        return 0.1

In this example, we have refactored the calculate_price function by extracting the logic for discount and tax rates into separate functions. This makes the code more modular and easier to read, as each function has a clear and specific purpose. Additionally, we have also used meaningful function and variable names to make the code more descriptive.

14. Use meaningful and consistent naming conventions for your files and directories

Suppose we have a project that involves building a web application using the Flask framework. We can follow these naming conventions:

  • Use lowercase letters and separate words with underscores for file names.
  • Use a consistent naming convention for module files, such as app.py, config.py, utils.py, etc.
  • Use a consistent naming convention for templates, such as base.html, index.html, login.html, etc.
  • Use a consistent naming convention for static assets, such as style.css, script.js, logo.png, etc.
  • Organize files and directories in a logical and consistent way, such as separating templates and static assets into separate directories.

Here's an example directory structure:

myapp/
    app.py
    config.py
    utils.py
    templates/
        base.html
        index.html
        login.html
    static/
        css/
            style.css
        js/
            script.js
        img/
            logo.png

By using consistent and meaningful naming conventions, our code is more organized and easier to navigate, which improves its readability and maintainability.

15. Use a consistent style guide and adhere to it

One popular style guide for Python is the PEP 8 style guide. Adhering to this style guide can help make your code more readable and consistent. Here's an example of using PEP 8 style conventions in Python:

# Use lowercase with underscores for variable names
first_name = "John"
last_name = "Doe"

# Use CamelCase for class names
class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
    
    # Use lowercase with underscores for function names
    def get_full_name(self):
        return f"{self.first_name} {self.last_name}"
    
# Use four spaces for indentation and no tabs
def print_person_info(person):
    print(f"First Name: {person.first_name}")
    print(f"Last Name: {person.last_name}")
    print(f"Full Name: {person.get_full_name()}")

# Use single quotes for string literals
print('Printing person information:')
print_person_info(Person(first_name, last_name))

In this example, we use lowercase with underscores for variable names, CamelCase for class names, lowercase with underscores for function names, four spaces for indentation, and single quotes for string literals, all in accordance with the PEP 8 style guide.

16. Avoid duplication of code by using functions and modules

# example.py
import math

def calculate_circle_area(radius):
    """Calculate the area of a circle."""
    return math.pi * radius ** 2

def calculate_rectangle_area(length, width):
    """Calculate the area of a rectangle."""
    return length * width

def main():
    # Calculate the area of a circle with a radius of 5
    circle_area = calculate_circle_area(5)
    print(f"The area of the circle is {circle_area}")

    # Calculate the area of a rectangle with length 6 and width 4
    rectangle_area = calculate_rectangle_area(6, 4)
    print(f"The area of the rectangle is {rectangle_area}")

if __name__ == "__main__":
    main()

In this example, we define two functions calculate_circle_area() and calculate_rectangle_area() that calculate the areas of a circle and a rectangle, respectively. We then use these functions in the main() function to calculate the areas of a circle with a radius of 5 and a rectangle with a length of 6 and a width of 4. By using functions to encapsulate the calculations, we avoid duplicating the code for the area calculations, which makes the code more maintainable and easier to read.

17. Use meaningful and descriptive comments to document your code

Bad code:

# Define a function to calculate the sum of two numbers
def sum_numbers(x, y):
    # Add the numbers and return the result
    return x + y

# Check if a number is positive or negative
def check_sign(num):
    # If the number is greater than or equal to zero, it is positive
    if num >= 0:
        return "Positive"
    # Otherwise, it is negative
    else:
        return "Negative"

In this example, the comments are not very helpful and do not provide any additional information beyond what is already evident in the code.

Refactored code:

def calculate_sum(x, y):
    """
    Calculates the sum of two numbers.

    Args:
    x (int): The first number.
    y (int): The second number.

    Returns:
    int: The sum of the two numbers.
    """
    return x + y

def check_sign(num):
    """
    Determines if a number is positive or negative.

    Args:
    num (int): The number to be checked.

    Returns:
    str: 'Positive' if the number is greater than or equal to zero, 'Negative' otherwise.
    """
    if num >= 0:
        return "Positive"
    else:
        return "Negative"

In this refactored example, the comments provide clear and concise information about what each function does, the arguments it takes, and what it returns. The comments follow the PEP 8 style guide for documentation, making the code more readable and easier to understand.

18. Use the appropriate data structures and algorithms for the problem at hand

Using appropriate data structures and algorithms can significantly improve the performance of your code. Here's an example of using the appropriate data structure and algorithm to solve a problem:

Let's say we have a list of integers and we want to find the maximum number in the list. One way to do this is by using a for loop to iterate through the list and keep track of the maximum number seen so far. However, this approach has a time complexity of O(n), where n is the length of the list.

A more efficient approach would be to use the built-in max() function, which has a time complexity of O(nlogn) in the worst case. Here's an example:

# Bad code
numbers = [1, 5, 2, 8, 3]
max_num = 0
for num in numbers:
    if num > max_num:
        max_num = num
print(max_num)

# Good code
numbers = [1, 5, 2, 8, 3]
max_num = max(numbers)
print(max_num)

In the bad code example, we are using a for loop to iterate through the list and keep track of the maximum number seen so far. This code works correctly, but it's not the most efficient way to solve the problem.

In the good code example, we're using the built-in max() function to find the maximum number in the list. This code is more concise and easier to read, and it has better performance than the previous example, especially for larger lists.

19. Write code that is easy to read and understand

# This program calculates the average of a list of numbers

def calculate_average(numbers):
    """
    Calculates the average of a list of numbers.

    Args:
        numbers: A list of numbers.

    Returns:
        The average of the list of numbers.
    """

    if not numbers:
        raise ValueError("Cannot calculate average of an empty list")

    total = sum(numbers)
    average = total / len(numbers)

    return average


# Example usage
numbers = [2, 4, 6, 8, 10]
average = calculate_average(numbers)
print(f"The average of {numbers} is {average}")

In this example, the code is easy to read and understand because:

  • The function calculate_average is named descriptively and the input arguments and return value are well-defined in the function's docstring.
  • The code inside the function is easy to follow and understand, with clear and concise variable names.
  • The program includes a clear error message if an empty list is passed as an argument.
  • The example usage at the end of the code demonstrates how to use the function and includes a descriptive print statement to output the results.

Overall, the code is easy to read and understand because it uses clear and consistent naming conventions, includes helpful comments, and follows best practices for code structure and organization.

20. Keep your code simple and concise, and avoid unnecessary complexity

An example of how to keep code simple and concise by using list comprehensions in Python:

# Bad code
def square_numbers(nums):
    result = []
    for num in nums:
        square = num ** 2
        result.append(square)
    return result

# Good code
def square_numbers(nums):
    return [num ** 2 for num in nums]

In the bad code example, we are using a for loop to iterate through the list of numbers, create a new variable to store the square of each number, and then append that variable to a new list. This code is longer and more complex than necessary.

In the good code example, we are using a list comprehension to achieve the same result in a more concise way. The code is easier to read and understand because we can see at a glance what it is doing.

21. Write unit tests for your code to ensure it works as expected and catch bugs early

import unittest

def calculate_area(width, height):
    return width * height

class TestCalculateArea(unittest.TestCase):
    def test_calculate_area(self):
        self.assertEqual(calculate_area(3, 4), 12)
        self.assertEqual(calculate_area(2.5, 6), 15)

if __name__ == '__main__':
    unittest.main()

22. Avoid long functions and limit the number of arguments they take

# bad example
def process_data(data, start_date, end_date, filters, sort_order):
    # lots of processing

# good example
def process_data(data):
    processed_data = preprocess_data(data)
    filtered_data = apply_filters(processed_data)
    sorted_data = sort_data(filtered_data)
    return sorted_data

23. Follow the Single Responsibility Principle (SRP) and make sure each function and class has one clear responsibility

# bad example
class DataProcessor:
    def process_data(data):
        # do lots of things with the data

# good example
class DataPreprocessor:
    def preprocess_data(data):
        # do some preprocessing

class DataProcessor:
    def process_data(data):
        processed_data = DataPreprocessor.preprocess_data(data)
        # do some processing on the preprocessed data
        return processed_data

24. Don't repeat yourself (DRY) and avoid duplicating code

# bad example
def calculate_area(width, height):
    return width * height

def calculate_volume(width, height, depth):
    return width * height * depth

# good example
def calculate_area(width, height):
    return width * height

def calculate_volume(width, height, depth):
    return calculate_area(width, height) * depth

25. Use consistent indentation and formatting to make code easy to read

# bad example
def calculate_area(width, height):
return width * height

# good example
def calculate_area(width, height):
    return width * height

26. Use exceptions to handle errors and edge cases, rather than returning error codes or None

# bad example
def divide(a, b):
    if b == 0:
        return None
    return a / b

# good example
def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("Cannot divide by zero")
    return a / b

27. Use exceptions rather than return codes

# Bad example - returning None in case of error
def divide(a, b):
    if b == 0:
        return None
    return a/b
    
result = divide(10, 0)
if result is None:
    print("Error: Division by zero")
    
# Good example - using an exception
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a/b
    
try:
    result = divide(10, 0)
except ValueError as e:
    print("Error:", str(e))

28. Keep code DRY (DRY)

# Bad example - duplicate code
def calculate_area_of_circle(radius):
    pi = 3.14
    area = pi * radius ** 2
    return area
    
def calculate_volume_of_sphere(radius):
    pi = 3.14
    volume = 4/3 * pi * radius ** 3
    return volume
    
# Good example - removing duplicate code
def calculate_area_of_circle(radius):
    pi = 3.14
    area = pi * radius ** 2
    return area
    
def calculate_volume_of_sphere(radius):
    area = calculate_area_of_circle(radius)
    volume = 4/3 * area * radius
    return volume

29. Encapsulating conditionals

Means to reduce the number of conditionals in the code by hiding them behind well-named functions or methods. This makes the code more readable and easier to understand, as the reader does not need to mentally parse complex conditional statements.

# Bad code:
if employee.department == 'Engineering' and employee.years_of_service > 10 and employee.skill_level > 5:
    # perform engineering tasks
elif employee.department == 'Marketing' and employee.years_of_service > 5 and employee.skill_level > 3:
    # perform marketing tasks
else:
    # perform generic tasks


# Good code:
class Employee:
    def __init__(self, department, years_of_service, skill_level):
        self.department = department
        self.years_of_service = years_of_service
        self.skill_level = skill_level

class EngineeringEmployee(Employee):
    def can_perform_tasks(self):
        return self.years_of_service > 10 and self.skill_level > 5

class MarketingEmployee(Employee):
    def can_perform_tasks(self):
        return self.years_of_service > 5 and self.skill_level > 3

class GenericEmployee(Employee):
    def can_perform_tasks(self):
        return True

# main code
employee = EngineeringEmployee('Engineering', 12, 6)
if employee.can_perform_tasks():
    # perform engineering tasks
else:
    employee = MarketingEmployee('Marketing', 6, 4)
    if employee.can_perform_tasks():
        # perform marketing tasks
    else:
        employee = GenericEmployee('Generic', 1, 1)
        # perform generic tasks

Python

Python Essentials 1 (PCEP)

Introduction to Python and computer programming

Data types, variables, basic I/O operations, and basic operators

Boolean values, conditional execution, loops, lists and list processing, logical and bitwise operations

Clean Code

Algorithms

Django

Django Rest Framework

API

pip

SQLAlchemy

FastAPI

Pytest

TDD

Git

Linux

Docker

Python Testing

Interview Questions

Clone this wiki locally