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(object):
    """ an item to buy """
    
    def __init__(self, name, quantity=1):
        if name not in INVENTORY:
            raise ValueError
        self.name = name
        self.quantity = quantity
        
    def __repr__(self):
        pass
        
    def __eq__(self, other):
        pass
    
    def __add__(self, other):
        pass

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
# will work
a += c
a
a == b
a == c

How do they behave in a list?

items = []
items.append(a)
items.append(b)
items
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/IPython/core/formatters.py:711, in PlainTextFormatter.__call__(self, obj)
    704 stream = StringIO()
    705 printer = pretty.RepresentationPrinter(stream, self.verbose,
    706     self.max_width, self.newline,
    707     max_seq_length=self.max_seq_length,
    708     singleton_pprinters=self.singleton_printers,
    709     type_pprinters=self.type_printers,
    710     deferred_pprinters=self.deferred_printers)
--> 711 printer.pretty(obj)
    712 printer.flush()
    713 return stream.getvalue()

File /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/IPython/lib/pretty.py:394, in RepresentationPrinter.pretty(self, obj)
    391 for cls in _get_mro(obj_class):
    392     if cls in self.type_pprinters:
    393         # printer registered in self.type_pprinters
--> 394         return self.type_pprinters[cls](obj, self, cycle)
    395     else:
    396         # deferred printer
    397         printer = self._in_deferred_types(cls)

File /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/IPython/lib/pretty.py:649, in _seq_pprinter_factory.<locals>.inner(obj, p, cycle)
    647         p.text(',')
    648         p.breakable()
--> 649     p.pretty(x)
    650 if len(obj) == 1 and isinstance(obj, tuple):
    651     # Special case for 1-item tuples.
    652     p.text(',')

File /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/IPython/lib/pretty.py:419, in RepresentationPrinter.pretty(self, obj)
    408                         return meth(obj, self, cycle)
    409                 if (
    410                     cls is not object
    411                     # check if cls defines __repr__
   (...)
    417                     and callable(_safe_getattr(cls, "__repr__", None))
    418                 ):
--> 419                     return _repr_pprint(obj, self, cycle)
    421     return _default_pprint(obj, self, cycle)
    422 finally:

File /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/IPython/lib/pretty.py:787, in _repr_pprint(obj, p, cycle)
    785 """A pprint that just redirects to the normal repr function."""
    786 # Find newlines and replace them with p.break_()
--> 787 output = repr(obj)
    788 lines = output.splitlines()
    789 with p.group():

TypeError: __repr__ returned non-string (type NoneType)
c in items
False

ShoppingCart#

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

class ShoppingCart(object):
    
    def __init__(self):
        self.items = []
        
    def subtotal(self):
        """ return a subtotal of our items """
        pass

    def add(self, name, quantity):
        """ add an item to our cart """
        pass
        
    def remove(self, name):
        """ remove all of item name from the cart """
        pass
        
    def report(self):
        """ print a summary of the cart """
        pass

Here are some tests

sc = ShoppingCart()
sc.add("orange", 19)
sc.add("apple", 2)
sc.report()
sc.add("apple", 9)
sc.report()
sc.subtotal()
sc.remove("apple")
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.