-
Notifications
You must be signed in to change notification settings - Fork 4
Clean Code Example in Python
# 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.
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
# 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
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
# 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
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.
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))
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.
# 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.
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.
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.
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.
# 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.
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.
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.
# 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.
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.
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.
# 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.
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.
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()
# 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
# 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
# bad example
def calculate_area(width, height):
return width * height
# good example
def calculate_area(width, height):
return width * height
# 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
# 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))
# 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
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
- Introduction
- Variables
- Data Types
- Numbers
- Casting
- Strings
- Booleans
- Operators
- Lists
- Tuple
- Sets
- Dictionaries
- Conditionals
- Loops
- Functions
- Lambda
- Classes
- Inheritance
- Iterators
- Multi‐Processing
- Multi‐Threading
- I/O Operations
- How can I check all the installed Python versions on Windows?
- Hello, world!
- Python literals
- Arithmetic operators and the hierarchy of priorities
- Variables
- Comments
- The input() function and string operators
Boolean values, conditional execution, loops, lists and list processing, logical and bitwise operations
- Comparison operators and conditional execution
- Loops
- [Logic and bit operations in Python]
- [Lists]
- [Sorting simple lists]
- [List processing]
- [Multidimensional arrays]
- Introduction
- Sorting Algorithms
- Search Algorithms
- Pattern-matching Algorithm
- Graph Algorithms
- Machine Learning Algorithms
- Encryption Algorithms
- Compression Algorithms
- Start a New Django Project
- Migration
- Start Server
- Requirements
- Other Commands
- Project Config
- Create Data Model
- Admin Panel
- Routing
- Views (Function Based)
- Views (Class Based)
- Django Template
- Model Managers and Querysets
- Form
- User model
- Authentification
- Send Email
- Flash messages
- Seed
- Organize Logic
- Django's Business Logic Services and Managers
- TestCase
- ASGI and WSGI
- Celery Framework
- Redis and Django
- Django Local Network Access
- Introduction
- API development
- API architecture
- lifecycle of APIs
- API Designing
- Implementing APIs
- Defining the API specification
- API Testing Tools
- API documentation
- API version
- REST APIs
- REST API URI naming rules
- Automated vs. Manual Testing
- Unit Tests vs. Integration Tests
- Choosing a Test Runner
- Writing Your First Test
- Executing Your First Test
- Testing for Django
- More Advanced Testing Scenarios
- Automating the Execution of Your Tests
- End-to-end
- Scenario
- Python Syntax
- Python OOP
- Python Developer position
- Python backend developer
- Clean Code
- Data Structures
- Algorithms
- Database
- PostgreSQL
- Redis
- Celery
- RabbitMQ
- Unit testing
- Web API
- REST API
- API documentation
- Django
- Django Advance
- Django ORM
- Django Models
- Django Views
- Django Rest Framework
- Django Rest Framework serializers
- Django Rest Framework views
- Django Rest Framework viewsets