Project: a shop inventory

Intermediate

Introduction

A shop needs to know what it has. In this project the data lives inside an object, and the only way to change it is through methods that check the rules, so the stock can never go negative.

This project keeps the data inside an object and makes every change go through a method. The result is a rule, that stock can never go negative, which the rest of the program simply cannot break.

How it works

A stock report

stock = {"pen": 7, "notebook": 4, "eraser": 0}
for item, amount in sorted(stock.items()):
    status = "out of stock" if amount == 0 else f"{amount} left"
    print(f"{item:<10}{status}")
Open in compiler

sorted(stock.items()) lists the items alphabetically, and a conditional expression picks the right status text for each one.

Make it yours

  • Add prices and a total_value() method.
  • Read commands such as add pen 10 or remove pen 3 from input (the practice problem does this).
  • Raise a custom OutOfStock exception instead of returning False, and catch it in the caller.

Syntax

class Inventory:
    def __init__(self):
        self.stock = {}

    def add(self, item, amount):
        self.stock[item] = self.stock.get(item, 0) + amount

Example

class Inventory:
    def __init__(self):
        self.stock = {}

    def add(self, item, amount):
        self.stock[item] = self.stock.get(item, 0) + amount

    def remove(self, item, amount):
        if self.stock.get(item, 0) < amount:
            return False
        self.stock[item] -= amount
        return True

shop = Inventory()
shop.add("pen", 10)
shop.add("notebook", 4)
print(shop.remove("pen", 3))
print(shop.remove("notebook", 9))
print(shop.stock)
Open in compiler
  • self.stock is a dictionary from item name to quantity.
  • add uses get(item, 0), so new items start from zero.
  • remove refuses to remove more than is in stock and returns False instead.

Step through shop.remove("pen", 3). The calculation box shows self.stock[item] - amount → 10 - 3 → 7, and the dictionary in the objects panel updates. The second removal asks for 9 notebooks when there are 4, so nothing changes.

Line by line

  1. Inventory() creates an empty stock dictionary.
  2. shop.add("pen", 10) and the next line add two items.
  3. shop.remove("pen", 3) has enough stock, so it subtracts and returns True.
  4. shop.remove("notebook", 9) asks for too many, so it returns False and changes nothing.
  5. The last line prints the dictionary, which shows the stock that remains.

Expected result

True
False
{'pen': 7, 'notebook': 4}

In the real world

Shops, warehouses and online stores all depend on software that protects exactly this rule. When two people buy the last item at the same moment, the check happens inside one place in the system, for the same reason as here.

Common mistakes

  • Changing shop.stock directly from outside the class, skipping the checks in remove.
  • Using self.stock[item] for an item that does not exist yet, which raises KeyError; get avoids it.
  • Forgetting to return a result from remove, so the caller cannot tell whether it worked.

Best practices

  • Keep the data private by convention, and change it only through methods.
  • Return a clear result, or raise a specific error.
  • Test the boundary, such as removing exactly what is in stock.

Summary

A class wrapped around a dictionary lets methods enforce rules such as "stock never goes below zero". Reports read the data, and every change goes through a method.

Practice

Add a restock_list(minimum) method that returns the items whose quantity is below minimum, sorted by name.

Quick check

1. Why does add use self.stock.get(item, 0)?
2. What does remove return when there is not enough stock?
3. Why keep the rules inside the class?

Key takeaways

  • Let methods be the only way to change the data.
  • A method can report success by returning True or False.
  • get avoids a KeyError for new items.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.