Answering your question and leveling up your code
You asked: "Do you have any tips for improving the readability of code?"
Today we'll answer that question directly, review your homework code, fix some bugs, and refactor your shop system into clean, readable code.
Your observation: "The store page has gotten a bit too cluttered for me to read it nicely."
This is a great observation! Recognizing when code needs cleanup is an important skill.
Variables and functions should describe what they hold or do.
| Instead of... | Use... |
|---|---|
cont |
is_shopping or continue_shopping |
flag |
is_valid_input or budget_entered |
cart (for item data) |
item_data or selected_item |
Use blank lines to separate logical sections. Group related code together.
Explain why, not what. The code shows what; comments explain reasoning.
Each function should do one thing. If a function is getting long, split it up.
Pick a style and stick to it. Same naming conventions, same spacing, same patterns.
Looking at your shop code, which of these 5 pillars could use the most improvement?
history class has proper structureback(), goback(), view() show initiativeIndexError in every method that could fail.strip().lower() used consistentlyclass history:
def __init__(self):
self.items = []
def enqueue(self, item):
self.items.append(item)
def back(self):
if self.is_empty():
raise IndexError("Cannot see an empty queue")
return self.items[-1]
def goback(self):
if self.is_empty():
raise IndexError("Cannot see an empty queue")
return self.items[-2]
# ... more methods
Nice touch: Your goback() method returns the second-to-last item - this shows you're thinking about how browser navigation actually works!
In your shop code, you wrote:
history = Undo
history.items(purchase)
history = Undo assigns the class itself to the variable, not an instance of it.
Think of it this way: Undo is a blueprint, Undo() builds something from that blueprint.
| Code | What It Does | Analogy |
|---|---|---|
history = Undo |
Assigns the class (blueprint) to history |
Holding the blueprint for a house |
history = Undo() |
Creates a new instance (object) and assigns it | Actually building a house from the blueprint |
class Dog:
def __init__(self):
self.name = "Unknown"
def bark(self):
print(f"{self.name} says woof!")
# This is the CLASS (blueprint)
print(Dog) # Output: <class '__main__.Dog'>
# This is an INSTANCE (actual dog object)
my_dog = Dog()
print(my_dog) # Output: <__main__.Dog object at 0x...>
# Only instances have the attributes set by __init__
my_dog.name = "Max"
my_dog.bark() # Output: Max says woof!
# The class itself doesn't have .name set
# Dog.bark() # ERROR! No 'self' to refer to
Undo(), what method runs automatically?__init__ runs automatically. It sets up self.items = [], giving the new instance its own empty list to work with.
Even after fixing Undo to Undo(), there's another issue:
history.items(purchase) # items is a LIST, not a method!
history.add(purchase) # add() is the method you defined
Look at where history = Undo appears in your code:
while cont == True:
# ... shopping loop ...
purchase = input("Please enter the item: ").strip().lower()
shopping_list.append(purchase)
price += cart[0]
history = Undo # <-- Created INSIDE the loop!
history.items(purchase)
# ... rest of loop ...
Even if you fix Undo to Undo(), creating it inside the loop means:
Every iteration creates a brand new instance with an empty items list. All previous items are lost!
undo_history = Undo() # Create ONCE, before the loop
while continue_shopping:
# ... shopping code ...
purchase = input("Please enter the item: ").strip().lower()
shopping_list.append(purchase)
price += item_data[0]
undo_history.add(purchase) # Use the existing instance
# ... rest of loop ...
Rule: If you want an object to persist across loop iterations, create it before the loop starts.
is vs inIn your browser history code:
if action_return is ["y", "yes", "1"]: # WRONG!
if action_return in ["y", "yes", "1"]: # Correct!
| Operator | What It Checks | Example |
|---|---|---|
is |
Same object in memory (identity) | a is b - Are a and b the exact same object? |
in |
Membership in a collection | "y" in ["y", "yes"] - Is "y" in this list? |
A string will never be the same object as a list, so is with a list will always be False.
Let's reorganize your shop code to be more readable. We'll use classes and functions to separate concerns.
What "things" exist in your shop?
class Cart:
"""Manages shopping cart with undo functionality."""
def __init__(self):
self.items = [] # List of purchased items
self.history = [] # For undo feature
def add_item(self, item_name, price):
"""Add an item to the cart."""
self.items.append(item_name)
self.history.append({'item': item_name, 'price': price})
print(f"Added {item_name} to cart")
def undo_last(self):
"""Remove the last added item. Returns the price refunded."""
if not self.history:
print("Nothing to undo!")
return 0
last_action = self.history.pop()
self.items.remove(last_action['item'])
print(f"Removed {last_action['item']} from cart")
return last_action['price']
def get_total(self):
"""Calculate total from history."""
return sum(action['price'] for action in self.history)
def show(self):
"""Display current cart contents."""
if not self.items:
print("Cart is empty")
else:
print(f"Cart: {', '.join(self.items)}")
print(f"Total: ${self.get_total():.2f}")
class Shop:
"""Manages store inventory and display."""
def __init__(self):
self.items = {
'apple': {'price': 1, 'description': "An apple a day keeps the doctor away"},
'toothpaste': {'price': 3, 'description': "Keep the dentist away"},
'toothbrush': {'price': 200, 'description': "What are you gonna brush with?"},
'watch': {'price': 50, 'description': "Time to get a watch"}
}
def display_items(self):
"""Show all available items."""
print("\n--- Available Items ---")
for name, data in self.items.items():
print(f" {name}: ${data['price']} - {data['description']}")
print()
def get_item(self, name):
"""Get item data by name. Returns None if not found."""
return self.items.get(name.lower())
def item_exists(self, name):
"""Check if an item exists in the shop."""
return name.lower() in self.items
def get_valid_budget():
"""Get a valid positive budget from user."""
while True:
try:
budget = float(input("Enter your budget: $"))
if budget <= 0:
print("Budget must be positive!")
continue
return budget
except ValueError:
print("Please enter a valid number")
def get_yes_no(prompt):
"""Get a yes/no response from user."""
response = input(prompt).strip().lower()
return response in ['y', 'yes', '1', 'true']
def calculate_discount(price, is_member):
"""Calculate discount based on price and membership."""
if is_member:
if price >= 100:
return price * 0.20
elif price >= 50:
return price * 0.10
else:
return price * 0.05
else:
if price >= 100:
return price * 0.10
return 0
def main():
"""Main shopping program."""
# Setup
shop = Shop()
cart = Cart()
# Get customer info
budget = get_valid_budget()
is_member = get_yes_no("Are you a member? (y/n): ")
print("\nWelcome to the store!")
# Shopping loop
shopping = True
while shopping:
shop.display_items()
cart.show()
print(f"Budget: ${budget:.2f}")
# Get purchase
choice = input("Enter item to buy (or 'done' to checkout): ").strip().lower()
if choice == 'done':
shopping = False
continue
# Validate item
if not shop.item_exists(choice):
print(f"'{choice}' not found in shop!")
continue
item = shop.get_item(choice)
# Check budget
if item['price'] > budget:
print("Not enough budget for this item!")
continue
# Make purchase
cart.add_item(choice, item['price'])
budget -= item['price']
# Offer undo
if get_yes_no("Undo this purchase? (y/n): "):
refund = cart.undo_last()
budget += refund
# Checkout
total = cart.get_total()
discount = calculate_discount(total, is_member)
final_price = total - discount
print("\n--- Checkout ---")
cart.show()
print(f"Discount: ${discount:.2f}")
print(f"Final price: ${final_price:.2f}")
# Run the program
if __name__ == "__main__":
main()
get_valid_budget()is_member, shopping, item| Bug | Problem | Fix |
|---|---|---|
history = Undo |
Assigns class, not instance | history = Undo() |
.items(purchase) |
items is a list, not a method | .add(purchase) |
| Instance inside loop | Resets every iteration | Create before loop |
is ["y", "yes"] |
Checks identity, not membership | in ["y", "yes"] |
is_shopping not cont| Class | Instance | |
|---|---|---|
| What is it? | A blueprint/template | An actual object built from the blueprint |
| Syntax | MyClass |
MyClass() |
| Has attributes? | Only class attributes | Has instance attributes from __init__ |
| Analogy | Recipe for cookies | An actual cookie you can eat |
Class vs Class()Build a virtual pet simulator from scratch! This homework will practice everything we covered today:
Pet()Click here to download the homework file
Pet classplay() method - uses energy, increases happinesssleep() method - restores energytime_passes() - hunger increases, energy decreasesPetShop class managing multiple petsRemember: Focus on readability! Use the checklist at the bottom of the homework file to review your code before our next lesson.
Next lesson: Now that you have a solid foundation with classes and readability, we'll explore more OOP concepts: inheritance (creating classes based on other classes) and how to design classes from scratch.