How to Optimize Code with Generative AI
Introduction
Generative AI can be an incredible tool for optimizing code. It can improve code quality, enhance existing code, and streamline architecture design. In this tutorial, we will see this in action by using ChatGPT to assist in optimizing a piece of Python code. To do this, we begin by setting the context with ChatGPT to establish a clear intent on our goal of optimizing code. Next, we will prompt ChatGPT to optimize our code in four categories: readability, maintainability, testability, and efficiency. We’ll conclude by reviewing the code to make sure ChatGPT’s assistance was useful.
Here is the code we will be optimizing:
class bank_account:def __init__(self, account, bal):self.account = accountself.bal = balself.hist = []def dep(self, amount):if amount > 0:self.bal += amountself.hist.append(f"Deposited ${amount}")else:return "Invalid deposit amount"def draw(self, amount):if 0 < amount <= self.bal:self.bal -= amountself.hist.append(f"Withdrew ${amount}")else:return "Invalid withdrawal amount"def stats(self):if self.bal >= 1000:return -1elif 500 <= self.bal < 1000:return -2else:return -3# Usage example:account = bank_account("12345", 1000)account.dep(500)account.draw(200)print("Transaction History:")print(account.hist)# Check the account statusstatus = account.stats()if status == -1:print("Your account is in good standing.")elif status == -2:print("Your account balance is below the recommended level.")else:print("Your account balance is low. Please consider depositing funds.")
You’ll see that most of the variable names are unfinished, if/else
statements are inefficiently used, and functions are not separated into individual functionality. We will use ChatGPT to assist in making the code more readable, maintainable, testable, and efficient.
Setting the Context for ChatGPT to Optimize Code
Let’s begin by setting the context for ChatGPT. We’ll prompt ChatGPT that it should be familiar with Python and that we require assistance. Then we’ll introduce our code to ChatGPT.
Prompt:
You are an expert in Python and I need help with some code.
Response:
Of course, I'd be happy to help you with your Python code. Please provide me with the details of the problem or the code you need assistance with, and I'll do my best to assist you.
At this step, we have provided the code to ChatGPT. Here is its response.
Response:
It looks like you've provided a Python code snippet for a simple bank account class. This class allows you to perform deposits, withdrawals, and check the account's status. Your code appears to be mostly correct, but there are a few minor improvements and suggestions I can provide:
1. Use better variable names: Consider using more descriptive variable names. For example, you can replace bal with balance and hist with transaction_history to make your code more readable.
2. Handle edge cases: Your code doesn't handle cases where a negative amount is passed to the dep or draw methods. You should consider raising an exception or handling these cases more gracefully.
3. Include a method to display the account balance: It would be helpful to include a method that allows you to check the account's current balance.
Here's an updated version of your code with these improvements:
'''
class BankAccount:
def __init__(self, account_number, balance):
self.account_number = account_number
self.balance = balance
self.transaction_history = []
def deposit(self, amount):
if amount > 0:
self.balance += amount
self.transaction_history.append(f"Deposited ${amount}")
else:
return "Invalid deposit amount"
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount
self.transaction_history.append(f"Withdrew ${amount}")
else:
return "Invalid withdrawal amount"
def get_balance(self):
return self.balance
def get_status(self):
if self.balance >= 1000:
return "Your account is in good standing."
elif 500 <= self.balance < 1000:
return "Your account balance is below the recommended level."
else:
return "Your account balance is low. Please consider depositing funds."
# Usage example:
account = BankAccount("12345", 1000)
account.deposit(500)
account.withdraw(200)
# Print the transaction history:
print("Transaction History:")
for transaction in account.transaction_history:
print(transaction)
# Check the account status
status = account.get_status()
print(status)
# Check the account balance
balance = account.get_balance()
print(f"Current Balance: ${balance}")
'''
With these improvements, your code is more readable, and it handles negative input amounts and includes a method to check the account's current balance.
ChatGPT is already trying to improve our code, especially with regard to readability! The code written does not follow good programming practices, as described before. Let’s delay the fixes as we will address them in the next section.
In some cases, ChatGPT can infer our code. However, this is not always the case, especially for code that solves a complex task. For this reason, it is good practice to provide ChatGPT with a brief summary of what our code is intended to do.
Refactoring the Code
Now that ChatGPT is familiar with our code, let’s prompt it to optimize the code. We will make a single request for each of the following areas of optimization:
Readability
Maintainability
Testability
Efficiency
Readable
Prompt:
Can you make the code more readable?
Response:
Certainly! I've made the code more readable by following PEP 8, the official Python style guide, and also added more descriptive variable names and improved the comments. Here's the revised code:
'''
class BankAccount:
def __init__(self, account_number, initial_balance):
self.account_number = account_number
self.balance = initial_balance
self.transaction_history = []
def deposit(self, amount):
if amount > 0:
self.balance += amount
self.transaction_history.append(f"Deposited ${amount}")
else:
return "Invalid deposit amount"
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount
self.transaction_history.append(f"Withdrew ${amount}")
else:
return "Invalid withdrawal amount"
def get_balance(self):
return self.balance
def get_status(self):
if self.balance >= 1000:
return "Your account is in good standing."
elif 500 <= self.balance < 1000:
return "Your account balance is below the recommended level."
else:
return "Your account balance is low. Please consider depositing funds."
# Usage example:
account = BankAccount("12345", 1000)
account.deposit(500)
account.withdraw(200)
# Print the transaction history:
print("Transaction History:")
for transaction in account.transaction_history:
print(transaction)
# Check the account status
status = account.get_status()
print(status)
# Check the account balance
balance = account.get_balance()
print(f"Current Balance: ${balance}")
'''
In this version, I've used more descriptive variable names, added comments, and formatted the code according to PEP 8 conventions for improved readability.
With respect to readability, ChatGPT modified variable names, added minor comments, and formatted the code in accordance with current Python programming standards. Compare this code to the original and you’ll notice that it is significantly easier to read.
Maintainable
Prompt:
Can you make the code more maintainable?
Response:
Certainly! To make the code more maintainable, you can further improve the organization and readability by separating concerns into methods and following best practices. Here's an updated version of the code with an improved structure and added docstrings for documentation:
'''
class BankAccount:
def __init__(self, account_number, initial_balance):
"""
Initialize a new bank account.
:param account_number: A unique account number.
:param initial_balance: The initial balance of the account.
"""
self.account_number = account_number
self.balance = initial_balance
self.transaction_history = []
def deposit(self, amount):
"""
Deposit funds into the account.
:param amount: The amount to deposit.
:return: None if the deposit is successful, or an error message.
"""
if amount > 0:
self.balance += amount
self.transaction_history.append(f"Deposited ${amount}")
else:
return "Invalid deposit amount"
def withdraw(self, amount):
"""
Withdraw funds from the account.
:param amount: The amount to withdraw.
:return: None if the withdrawal is successful, or an error message.
"""
if 0 < amount <= self.balance:
self.balance -= amount
self.transaction_history.append(f"Withdrew ${amount}")
else:
return "Invalid withdrawal amount"
def get_balance(self):
"""
Get the current balance of the account.
:return: The current balance.
"""
return self.balance
def get_status(self):
"""
Get the status of the account based on the balance.
:return: A status message.
"""
if self.balance >= 1000:
return "Your account is in good standing."
elif 500 <= self.balance < 1000:
return "Your account balance is below the recommended level."
else:
return "Your account balance is low. Please consider depositing funds"
def print_transaction_history(self):
"""
Print the transaction history of the account.
"""
print("Transaction History:")
for transaction in self.transaction_history:
print(transaction)
# Usage example:
account = BankAccount("12345", 1000)
account.deposit(500)
account.withdraw(200)
account.print_transaction_history()
status = account.get_status()
print(status)
balance = account.get_balance()
print(f"Current Balance: ${balance}")
'''
In this version, I've added docstrings to describe the purpose of each method, making the code more self-documenting. I've also separated the printing of the transaction history into a dedicated method for cleaner code organization. This should make the code more maintainable and easier to understand.
With respect to maintainability, it split the print_transaction_history()
into a single function for cleaner code. This allows us to call print_transaction_history()
instead of typing out the print statement inside each function. Additionally, it added comments to provide clarity.
Testable
Prompt:
Can you make the code more testable?
Response:
To make the code more testable, you can isolate the core logic from I/O operations, such as printing to the console. This separation allows you to write unit tests without having to interact with the console. Here's a modified version of the code with these changes:
'''
class BankAccount:
def __init__(self, account_number, initial_balance):
self.account_number = account_number
self.balance = initial_balance
self.transaction_history = []
def deposit(self, amount):
if amount > 0:
self.balance += amount
self.transaction_history.append(f"Deposited ${amount}")
else:
return "Invalid deposit amount"
def withdraw(self, amount):
if 0 < amount <= self.balance:
self.balance -= amount
self.transaction_history.append(f"Withdrew ${amount}")
else:
return "Invalid withdrawal amount"
def get_balance(self):
return self.balance
def get_status(self):
if self.balance >= 1000:
return "Your account is in good standing."
elif 500 <= self.balance < 1000:
return "Your account balance is below the recommended level."
else:
return "Your account balance is low. Please consider depositing funds"
def get_transaction_history(self):
return self.transaction_history
# Function to display the transaction history
def display_transaction_history(transaction_history):
print("Transaction History:")
for transaction in transaction_history:
print(transaction)
# Usage example:
if __name__ == "__main__":
account = BankAccount("12345", 1000)
account.deposit(500)
account.withdraw(200)
transaction_history = account.get_transaction_history()
display_transaction_history(transaction_history)
status = account.get_status()
print(status)
balance = account.get_balance()
print(f"Current Balance: ${balance}")
'''
In this updated code, I've introduced a display_transaction_history function to print the transaction history, allowing you to test the BankAccount class separately. This separation of concerns makes it easier to write unit tests for the core functionality of the BankAccount class without directly relying on console I/O.
With respect to testability, there weren’t many changes from the previous fix because the code was already separated for maintenance purposes and the code base is too small to need more changes. In larger coding projects, testing and maintaining code are entirely different. You’ll see further separation and modulation of functions into smaller blocks of code for testing purposes.
Efficient
Prompt:
Can you make the code more efficient?
Response:
Your original code is quite efficient for its intended purpose, which is to model a simple bank account. However, there are always ways to optimize code for performance and resource usage, but these optimizations often depend on the specific use case. In the case of your bank account class, the code is already quite efficient because it doesn't involve complex calculations or operations.
If you have specific performance requirements or if you'd like to optimize the code for a particular use case, please provide more details about the performance concerns you have in mind. Without specific requirements or performance bottlenecks, it's challenging to provide further optimizations for this code.
In general, you can consider the following tips for writing efficient Python code:
1. Avoid unnecessary data copies: Minimize unnecessary copying of data structures, especially for large data sets.
2. Use built-in functions and libraries: Python's built-in functions and libraries are often optimized for performance.
3. Profile your code: Use profiling tools to identify performance bottlenecks in your code.
4. Optimize algorithms: If your code involves complex algorithms, focus on optimizing those algorithms rather than micro-optimizations.
5. Caching and memoization: Use caching and memoization to store and reuse the results of expensive operations when applicable.
6. Data structures: Choose appropriate data structures for your specific use case.
If you have a specific optimization question or performance concern in your code, please provide more details, and I'd be happy to provide more targeted advice.
With respect to efficiency, and after the modifications through the other optimizations, there were no code changes.
Conclusion
Great work! We used generative AI, specifically ChatGPT, to optimize our code. We first set the context for optimizing our Python code. Next, we prompted ChatGPT to improve our code in readability, maintainability, testability, and efficiency! We noticed that most of the code changed in the readability section and very few changes were made in the other areas.
Depending on the generative AI used, the resulting code may be different. For example, ChatGPTv3 is only trained on data up to September 2021. Advancements in programming languages occur frequently. Remember that generative AI is a tool and not a solution!
If you’re interested in learning more about how to use generative AI to optimize your code, check out our Case Study for Code Optimization!
If you’re interested in reading more about how generative AI can be applied in your daily life, please check out our AI Catalog of articles!
Author
The Codecademy Team, composed of experienced educators and tech experts, is dedicated to making tech skills accessible to all. We empower learners worldwide with expert-reviewed content that develops and enhances the technical skills needed to advance and succeed in their careers.
Meet the full team