Skip to content
Theory · Final project · lesson 7 of 8

The final project

Picture a storekeeper with a notebook. Each line of the notebook holds an item name and how much of it is on the shelves. Apples arrive, and the storekeeper adds them to the "apples" line. Someone asks for more than there is, and the storekeeper refuses and changes nothing in the notebook.

A class in a real program usually rests on several ideas at once. For example, it keeps data in a dict, changes it through methods and raises an exception when an operation is impossible. The final exercise works the same way: it introduces no new constructs; you combine familiar ones.

After it, the course is over, and the next step is the Python interview simulator. The "Practice a Python interview" button on the course completion page opens it. The simulator asks about the course's topics and about those the course did not touch.

The warehouse

The exercise is a warehouse that counts the units of each item. The storekeeper's notebook here is a dict in the class Inventory:

  • the key is an item name;
  • the value is a number of units.

If an item cannot be taken, the take method raises the OutOfStockError exception and leaves the warehouse unchanged. The class OutOfStockError is already declared in the stub, and the tests catch only this class: with another exception, the test fails.

Part of the exerciseWhat it needs
__init__an instance attribute holding a dict
add and countget with a default value
takea comparison, raise, del
namessorted

Three outcomes of take

The trickiest method is take: it has three outcomes, and the tests check each one.

  • There is less of the item than asked for, or none at all: take raises OutOfStockError, and the quantity does not change.
  • Exactly the amount on hand is taken: the item leaves the dict, and names no longer returns it.
  • There is more of the item than asked for: the quantity goes down by qty.

This is where the notebook analogy stops. A storekeeper may keep the line "apples: 0." Our warehouse has no such line: an item taken out in full is removed from the dict.

How to proceed

Work step by step:

  1. Start with __init__: without the dict, the other methods have nothing to work with.
  2. Then write one method at a time and run the tests after each.
  3. Almost every test calls add first, so a bug in add fails many rows at once. Fix it first.

The stub's methods return None, so at first none of the eight tests passes. The row test_add_then_count shows got None, and a test that expects an exception shows no OutOfStockError, got None.

Common mistake

The dict is declared as a class attribute, not an instance one:

Python
class Inventory:
    items = {}  # ✗

    def add(self, name, qty):
        self.items[name] = self.items.get(name, 0) + qty

    def count(self, name):
        return self.items.get(name, 0)


shop = Inventory()
home = Inventory()
shop.add("apple", 5)
print(home.count("apple"))  # 5

There is no error, but the warehouse home sees the apples put into shop. Let's see why:

  1. The line items = {} in the class body creates one dict, a class attribute.
  2. The instance shop has no items attribute of its own, so self.items finds the class's dict.
  3. The line self.items[name] = … assigns to a key of that dict, not to an attribute of the instance.
  4. home.count("apple") reads the same dict and prints 5.

A class attribute is one for all instances, and both warehouses write in one notebook. Each test creates its own warehouse, but with this attribute it gets the items from the earlier tests. Create the dict in __init__: self.items = {}.

Interviewers like to ask how a class attribute differs from an instance attribute. The answer: a class attribute is one for all instances, while each instance has its own attribute, created through self in __init__.

Key points

  • The warehouse keeps the quantities in a dict: the key is the item, the value a number of units.
  • The dict is created in __init__ as self.items = {}; otherwise it is one for all instances.
  • When an item runs short, take raises OutOfStockError and leaves the warehouse unchanged.
  • An item taken out in full leaves the dict and names.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way