Перейти к содержимому
Теория · Итоговый проект · урок 7 из 8

Итоговый проект

Представьте кладовщика с тетрадью. На каждой строке тетради — название товара и сколько его лежит на полках. Привезли яблоки — он прибавляет их к строке «яблоки». Просят больше, чем есть, — он отказывает и в тетради ничего не меняет.

Класс в настоящей программе обычно опирается сразу на несколько идей. Например, он хранит данные в словаре, меняет их методами и возбуждает исключение, когда операцию выполнить нельзя. Итоговая задача устроена так же: новых конструкций в ней нет, нужно соединить знакомые.

После неё курс закончен, и следующий шаг — симулятор собеседований по Python. Его открывает кнопка «Пройти собеседование по Python» на странице завершения курса. Симулятор спрашивает и о темах курса, и о тех, которых курс не касался.

Склад

Задача — склад, который считает единицы каждого товара. Тетрадь кладовщика здесь — словарь в классе Inventory:

  • ключ — название товара;
  • значение — число единиц.

Если забрать товар нельзя, метод take возбуждает исключение OutOfStockError и склад не меняет. Класс OutOfStockError уже объявлен в заготовке, и тесты ловят только его: с другим исключением тест не пройдёт.

Часть задачиЧто понадобится
__init__атрибут экземпляра со словарём
add и countget со значением по умолчанию
takeсравнение, raise, del
namessorted

Три исхода take

Самый хитрый метод — take: у него три исхода, и тесты проверяют каждый.

  • Товара меньше, чем просят, или его нет совсем: take возбуждает OutOfStockError, и количество не меняется.
  • Забирают ровно столько, сколько есть: товар исчезает из словаря, и names его больше не возвращает.
  • Товара больше, чем просят: количество уменьшается на qty.

Здесь аналогия с тетрадью заканчивается. Кладовщик может оставить строку «яблоки — 0». В нашем складе такой строки нет: товар, забранный целиком, удаляют из словаря.

Порядок работы

Работайте по шагам:

  1. Начните с __init__: без словаря остальным методам не с чем работать.
  2. Затем пишите по одному методу и после каждого запускайте тесты.
  3. Почти каждый тест сначала вызывает add, поэтому ошибка в add проваливает сразу много строк. Исправляйте её первой.

Методы заготовки возвращают None, поэтому сначала не проходит ни один из восьми тестов. Строка test_add_then_count показывает got None, а тест, который ждёт исключения, — no OutOfStockError, got None.

Частая ошибка

Словарь объявили атрибутом класса, а не экземпляра:

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

Ошибки нет, но склад home видит яблоки, положенные в shop. Давайте разберём, почему:

  1. Строка items = {} в теле класса создаёт один словарь — атрибут класса.
  2. У экземпляра shop своего атрибута items нет, поэтому self.items находит словарь класса.
  3. Строка self.items[name] = … присваивает значение ключу этого словаря, а не атрибуту экземпляра.
  4. home.count("apple") читает тот же словарь и печатает 5.

Атрибут класса один на все экземпляры, и оба склада пишут в одну тетрадь. Каждый тест создаёт свой склад, но с таким атрибутом получает товары из предыдущих тестов. Создайте словарь в __init__: self.items = {}.

На собеседовании часто спрашивают, чем атрибут класса отличается от атрибута экземпляра. Ответ: атрибут класса один на все экземпляры, а атрибут экземпляра у каждого свой, и его создают через self в __init__.

Главное

  • Склад хранит количества в словаре: ключ — товар, значение — число единиц.
  • Словарь создают в __init__ как self.items = {}, иначе он один на все экземпляры.
  • При нехватке товара take возбуждает OutOfStockError и склад не меняет.
  • Товар, забранный целиком, исчезает из словаря и из names.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом