Start learning free

LESSON 557 OF 569 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Inventory Item in Python: Protecting a Stock Count

Practice O(1) Read the full explanation

A shop keeps track of how many pieces of each item it has: the item's stock. The stock can never go below 0, and it changes only when pieces are sold or new ones arrive. So the stock is a private field, changed only through two methods that check the rules. The item's name has no rules to protect, so it stays a field any code can read.

Implement the class InventoryItem:

  • InventoryItem(name, stock) creates an item with that name and that many pieces in stock. The name is kept in a field name that other code can read.
  • sell(n) sells n pieces. If n is at least 1 and at most the stock, it lowers the stock by n and returns True. Otherwise it changes nothing and returns False: selling 0 pieces, a negative number of pieces, or more pieces than there are in stock is refused.
  • restock(n) adds n pieces to the stock when n is at least 1, and ignores n otherwise. It returns nothing.
  • getStock() returns the stock. It is the only way for other code to read it.

Keep the stock private the way the Access Modifiers lesson does in your language: self._stock in Python (private by convention), a #stock field in JavaScript, and the private keyword in Java, C++, C#, TypeScript, Kotlin, Scala and Swift. In Kotlin and Scala the constructor's second parameter is named initialStock, and the private stock starts at its value.

Each test case creates one item, then applies a list of operations to it, in order. The output has one line per test case: the item's name and a colon, then what each sell and getStock call returns, separated by spaces, with sell's answer printed as True or False. The starter code spells these names the way your language does: get_stock in Python, Sell, Restock and GetStock in C#.

 

Example 1:

Input:
    InventoryItem("Pencil", 5)
    sell(3)
    getStock()
    sell(5)
    getStock()
    restock(10)
    getStock()
Output: Pencil: True 2 False 2 12
Explanation: Selling 3 of the 5 pencils works and leaves 2. Selling 5 more is refused, because only 2 are left,
and the stock stays 2. restock(10) brings it to 12.

Example 2:

Input:
    InventoryItem("Mug", 4)
    sell(4)
    getStock()
    sell(1)
    getStock()
Output: Mug: True 0 False 0
Explanation: Selling all 4 mugs is allowed and leaves 0. With nothing left, even one mug can't be sold.

Example 3:

Input:
    InventoryItem("Lamp", 2)
    sell(0)
    sell(-3)
    restock(0)
    restock(-5)
    getStock()
    restock(1)
    sell(3)
    getStock()
Output: Lamp: False False 2 True 0
Explanation: Selling 0 or -3 lamps is refused, and restocking 0 or -5 lamps is ignored, so the stock is still 2.
After restock(1) there are 3 lamps, and selling all 3 works.

 

Constraints:

  • A name is one word of English letters. 0 <= stock <= 104 when the item is created.
  • -104 <= n <= 104 in every sell and restock.
  • A test case has from 1 to 1000 operations, and at least one of them is a sell or a getStock.
STEP 1 OF 6
    Ctrl + Enter to check

    PYTHON · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Inventory Item in Python, explained

    Problem overview

    A shop tracks how many pieces of each item it has. The stock can never go below 0, and it changes only when pieces are sold or new ones arrive. So the class InventoryItem keeps the stock private and changes it only through two methods that check the rules. The name has no rules to protect, so it stays a plain attribute that any code can read.

    • InventoryItem(name, stock) creates an item with that name and that many pieces.
    • sell(n) lowers the stock by n and returns True when n is at least 1 and at most the stock. Otherwise it changes nothing and returns False.
    • restock(n) adds n to the stock when n is at least 1, and ignores it otherwise. It returns nothing.
    • get_stock() returns the stock.

    With 5 pencils, sell(3) returns True and leaves 2. Then sell(5) returns False, because only 2 are left, and the stock stays 2. After restock(10) there are 12. The lesson's tests print the booleans as True and False.

    Approach: check first, change second

    sell must refuse three kinds of request: 0 pieces, a negative number of pieces and more pieces than are in stock. All three are caught by one test: n < 1 or n > self._stock. The check comes before any change, so a refused sale leaves the stock exactly as it was. Only a sale that passes gets to the line that lowers the stock, so the stock can't go below 0.

    Selling exactly the whole stock is allowed. That is why the upper test is n > self._stock and not n >= self._stock.

    restock is the mirror image. A delivery of 0 or a negative number isn't a delivery, so the method ignores it. A single if n >= 1 around the addition does it.

    Step by step

    1. Store the name in self.name. It has no rule to protect, so other code may read it directly.
    2. Store the stock in self._stock. The underscore is Python's convention for "private": other code should go through the methods.
    3. In sell, write the refusal first: an if for n below 1 or above self._stock, whose body returns False.
    4. Let the sale through: lower self._stock by n and return True.
    5. In restock, add n to self._stock only when n >= 1.
    6. get_stock returns self._stock. Other code can see the stock through it but never set it.

    The solution in Python

    class InventoryItem:
        def __init__(self, name, stock):
            self.name = name
            self._stock = stock
        def sell(self, n):
            if n < 1 or n > self._stock:
                return False
            self._stock -= n
            return True
        def restock(self, n):
            if n >= 1:
                self._stock += n
        def get_stock(self):
            return self._stock
    

    Using it:

    item = InventoryItem("Lamp", 2)
    print(item.sell(0), item.sell(-3))
    item.restock(0)
    item.restock(-5)
    print(item.get_stock())
    item.restock(1)
    print(item.sell(3), item.get_stock())
    
    False False
    2
    True 0
    

    Both refused sales and both ignored deliveries leave the stock at 2. After restock(1) there are 3 lamps, and selling all 3 works.

    Python details

    • A method without return gives None. restock returns nothing, so print(item.restock(3)) prints None.
    • or stops early. In n < 1 or n > self._stock, if the first part is true the second isn't evaluated. Either way the result is a refusal.
    • Private by convention. Python doesn't block item._stock = -50 from outside. The underscore asks other code not to do it, and the class stays correct as long as everyone follows that.

    Edge cases and common mistakes

    • Selling the whole stock. With 4 mugs, sell(4) returns True and leaves 0. After that even sell(1) returns False.
    • Checking after the change. If sell lowered the stock first and tested afterwards, a refused sale would already have changed it.
    • Using and instead of or. No number is both below 1 and above the stock, so the refusal would never fire.
    • Returning a value from restock. The lesson says it returns nothing.
    • Complexity. Each method is a comparison and one addition or subtraction, so each runs in O(1) time.
    Keep going with Python500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free