Practicing Classes#

Exercise 1 (shopping cart)#

Let’s write a simple shopping cart class – this will hold items that you intend to purchase as well as the amount, etc. And allow you to add / remove items, get a subtotal, etc.

We’ll use two classes: Item will be a single item and ShoppingCart will be the collection of items you wish to purchase.

First, our store needs an inventory – here’s what we have for sale:

INVENTORY_TEXT = """
apple, 0.60
banana, 0.20
grapefruit, 0.75
grapes, 1.99
kiwi, 0.50
lemon, 0.20
lime, 0.25
mango, 1.50
papaya, 2.95
pineapple, 3.50
blueberries, 1.99
blackberries, 2.50
peach, 0.50
plum, 0.33
clementine, 0.25
cantaloupe, 3.25
pear, 1.25
quince, 0.45
orange, 0.60
"""

# this will be a global -- convention is all caps
INVENTORY = {}
for line in INVENTORY_TEXT.splitlines():
    if line.strip() == "":
        continue
    item, price = line.split(",")
    INVENTORY[item] = float(price)
INVENTORY
{'apple': 0.6,
 'banana': 0.2,
 'grapefruit': 0.75,
 'grapes': 1.99,
 'kiwi': 0.5,
 'lemon': 0.2,
 'lime': 0.25,
 'mango': 1.5,
 'papaya': 2.95,
 'pineapple': 3.5,
 'blueberries': 1.99,
 'blackberries': 2.5,
 'peach': 0.5,
 'plum': 0.33,
 'clementine': 0.25,
 'cantaloupe': 3.25,
 'pear': 1.25,
 'quince': 0.45,
 'orange': 0.6}

Item#

Here’s the start of an item class – we want it to hold the name and quantity.

You should have the following features:

  • the name should be something in our inventory

  • Our shopping cart will include a list of all the items we want to buy, so we want to be able to check for duplicates. Implement the equal test, ==, using __eq__

  • we’ll want to consolidate dupes, so implement the + operator, using __add__ so we can add items together in our shopping cart. Note, add should raise a ValueError if you try to add two Items that don’t have the same name.

Here’s a start:

class Item:
    """ an item to buy """
    
    def __init__(self, name, quantity=1):
        """keep track of an item that is in our inventory"""
        if name not in INVENTORY:
            raise ValueError("invalid item name")
        self.name = name
        self.quantity = quantity
        
    def __repr__(self):
        return f"{self.name}: {self.quantity}"
        
    def __eq__(self, other):
        """check if the items have the same name"""
        return self.name == other.name
    
    def __add__(self, other):
        """add two items together if they are the same type"""
        if self.name == other.name:
            return Item(self.name, self.quantity + other.quantity)
        else:
            raise ValueError("names don't match")

Here are some tests your code should pass:

a = Item("apple", 10)
b = Item("banana", 20)
c = Item("apple", 20)
# won't work
a + b
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[6], line 2
      1 # won't work
----> 2 a + b

Cell In[3], line 23, in Item.__add__(self, other)
     21     return Item(self.name, self.quantity + other.quantity)
     22 else:
---> 23     raise ValueError("names don't match")

ValueError: names don't match
# will work
a += c
print(a)
apple: 30
d = Item("dog")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[8], line 1
----> 1 d = Item("dog")

Cell In[3], line 7, in Item.__init__(self, name, quantity)
      5 """keep track of an item that is in our inventory"""
      6 if name not in INVENTORY:
----> 7     raise ValueError("invalid item name")
      8 self.name = name
      9 self.quantity = quantity

ValueError: invalid item name
# should be False
a == b
False
# should be True -- they have the same name
a == c
True

How do they behave in a list?

items = []
items.append(a)
items.append(b)
items
[apple: 30, banana: 20]
# should be True -- they have the same name
c in items
True

ShoppingCart#

Now we want to create a shopping cart. The main thing it will do is hold a list of items.

class ShoppingCart:
    
    def __init__(self):
        # the list of items we control
        self.items = []
        
    def subtotal(self):
        """ return a subtotal of our items """
        pass

    def add(self, name, quantity):
        """ add an item to our cart -- the an item of the same name already
        exists, then increment the quantity.  Otherwise, add a new item
        to the cart with the desired quantity."""
        pass
        
    def remove(self, name):
        """ remove all of item name from the cart """
        pass
        
    def report(self):
        """ print a summary of the cart """
        for item in self.items:
            print(f"{item.name} : {item.quantity}")

Here are some tests

sc = ShoppingCart()
sc.add("orange", 19)
sc.add("apple", 2)
sc.report()
sc.add("apple", 9)
# apple should only be listed once in the report, with a quantity of 11
sc.report()
sc.subtotal()
sc.remove("apple")
# apple should no longer be listed
sc.report()

Exercise 2: Poker Odds#

Use the deck of cards class from the notebook we worked through outside of class to write a Monte Carlo code that plays a lot of hands of straight poker (like 100,000). Count how many of these hands has a particular poker hand (like 3-of-a-kind). The ratio of # of hands with 3-of-a-kind to total hands is an approximation to the odds of getting a 3-of-a-kind in poker.

You’ll want to copy-paste those classes into a .py file to allow you to import and reuse them here

Exercise 3: Tic-Tac-Toe#

Revisit the tic-tac-toe game you developed in the functions exercises but now write it as a class with methods to do each of the main steps.