As an exercise in revisioning what an applied digital mathematics curriculum might look like, I came up with my Silicon Forest Digital Mathematics, which I've been sharing with teachers, and testing with students.
The Silicon Forest is a great place for distilled maths and computer science. The original Wiki was invented here.
DM distills into four areas:
One would not need to adopt the above as a complete replacement for anything. As an outline and self-contained rubric, it's a source of organizing ideas, or "heuristics for teachers" as I call it.
In this Sandbox I want to look at a Supermarket simulation. A goal here is to demonstrate object oriented thinking, by making the Supermarket, Shopper and Inventory be three distinct objects.
The first code cell demonstrates a best practice, which is to clone the Exception type to define subclasses of error specific to the model at hand.
The two errors we seek to handle are:
"""
Created on Fri Sep 30 10:10:41 2016
@author: Kirby Urner
Show some types working together to simulate a shopper
in a Supermarket, with a fixed starting amount of money.
Inventory keeps track of how much of a product remains.
Supermarket keeps track of income, should equal sum of purchases.
"""
import json
class NoMoney(Exception):
pass
class OutOfStock(Exception):
pass
The Supermarket class is a designed as a context manager meaning it has __enter__
and __exit__
methods triggered by the keyword with
.
See the simulation function below, wherein the shopper's transactions happen within a try block, under which any exceptions get handled.
class SuperMarket:
"""
Persists buyable items in a json file.
Initializes with 0 cash
"""
def __init__(self, source):
self.source = source
def __enter__(self):
self.inventory = Inventory(self.source)
self.cash = 0
return self
def buy(self, shopper, item, how_many):
"""
remove money from shopper wallet, add qty of item
to basket, abort if customer short on cash
"""
if item in self.inventory.wares: # check keys
price = self.inventory.wares[item][0]
try:
self.inventory.remove_item(item, how_many)
shopper.add_item(item, price, how_many)
self.cash += price * how_many
except NoMoney:
# print("Customer out of money")
raise # re-raise exception
except OutOfStock:
# print("Don't have enough in stock")
raise
def __exit__(self, *oops):
"""
write json file
"""
self.inventory.save_items()
def __repr__(self):
return "SuperMarket with cash: {}".format(self.cash)
The shopper's name is somewhat incidental and would like be a primary key id number pointing to another database of all known shoppers.
A contemporary supermarket might work with each shopper through a smartphone app, offering custom discounts based on previous buying patterns. Our model here makes no attempt to take these added wrinkles into account.
The SuperMarket class contains the core method: buy
. The shopper is but an argument to this method, however the shopper has an add_item
method the Supermarket.buy
method will, in turn, call.
Note that when an Exception is raised, as in the case of raise NoMoney
, the remainder of the method in question is not executed. The flow of execution, in case of an exception, is to escape the surrounding try block immediately, and to fall through to the first matching Exception.
class Shopper:
def __init__(self, name, budget):
self.name = name
self.basket = { }
self.wallet = budget # budgeted allowance
def add_item(self, item, price, qty):
"""
add qty of item to basket and pay, if money available
"""
if self.wallet - qty * price < 0:
raise NoMoney
self.basket[item] = self.basket.get(item, 0) + qty
self.wallet -= qty * price
def __repr__(self):
return "{}: {}; {}".format(self.name, self.wallet, self.basket)
Just as the Shopper has the ability to raise an OutOfMoney
exception, so does the Inventory
type get to raise an OutOfStock
.
class Inventory:
"""
Supermarket brings inventory instance on board upon
initialization, increments / decrements items, reads
and writes to json file. Does not track cash.
"""
def __init__(self, the_file):
print("Loading inventory...")
self.storage = the_file
with open(the_file, 'r') as warehouse:
self.wares = json.load(warehouse)
def save_items(self):
print("Saving inventory...")
with open(self.storage, 'w') as warehouse:
json.dump(self.wares, warehouse)
def remove_item(self, item, qty):
if qty > self.wares[item][1]:
raise OutOfStock
self.wares[item][1] -= qty
def add_item(self, item, qty):
self.wares[item][1] += qty
The test data we're using is quite small and simply, but wouldn't have to be.
You have the freedom to cut and paste this test_data and simulation code to a cell below, and then modify both.
Play around!
Learn the ropes!
def test_data():
stuff = {
"Snicker-Snacks": [5.99, 10],
"Polly's Peanuts": [3.99, 10],
"Dr. Soap": [4.99, 10]}
with open("the_stuff.json", 'w') as warehouse:
json.dump(stuff, warehouse, indent=4)
def simulation():
test_data() # initialize the_stuff.json
kirby = Shopper("Kirby", 200)
with SuperMarket("the_stuff.json") as market:
try:
market.buy(kirby, "Snicker-Snacks", 2)
market.buy(kirby, "Polly's Peanuts", 2)
market.buy(kirby, "Dr. Soap", 11) # triggers exception
except NoMoney:
print("Uh oh, out of money!")
except OutOfStock:
print("Uh oh, out of stock!")
else:
print(kirby)
print(market.inventory.wares)
print(market)
simulation()
Loading inventory... Uh oh, out of stock! Saving inventory...