Solutions: You can find the file with solutions for all questions here.

### Question 1: Arr88

In lab you created the T88ble, now you will create arr88, which are similar to numpy arrays from Data 8.

Complete the `__len__`, and `item` functions according to the docstrings.

`__len__` is a special attribute, like `__init__` that allows us to call `len` on our Arr88s to get their length!

``````    def __len__(self):
""" Return the length of the Arr88

>>> arr88 = Arr88([1, 2, 3])
>>> len(arr88)
3
>>> arr88 = Arr88([1, 2, 3, 4])
>>> len(arr88)
4
"""
return len(self._values)

def item(self, i):
"""
Get the item of the Arr88 at index i
>>> arr88 = Arr88([1, 2, 3])
>>> arr88.item(1)
2
>>> arr88.item(0)
1
"""
return self._values[i]``````

Use OK to test your code:

``python3 ok -q Arr88.__len__ --local``

Use OK to test your code:

``python3 ok -q Arr88.item --local``

Complete the `__add__`, `__mul__`, and `negate` functions according to the docstrings.

Keep an eye out for which functions mutate the Arr88 and which don't!

`__add__` and `__mul__` are also special attributes, like `__init__` and `__len__`, that allow us to use `+` and `*` on our Arr88s to add/multiply them componentwise!

``````    def __add__(self, arr88):
""" Add two Arr88s of the same length element by element

>>> arr88a = Arr88([1, 2, 3])
>>> arr88b = Arr88([4, 5, 6])
>>> arr88a + arr88b
Arr88([5, 7, 9])
>>> arr88a # We aren't mutating arr88a
Arr88([1, 2, 3])
>>> arr88a = Arr88(['He', 'Wor', '!'])
>>> arr88b = Arr88(['llo', 'ld', ''])
>>> arr88a + arr88b
Arr88(['Hello', 'World', '!'])
"""
# Checks that the lengths are the same
assert len(self) == len(arr88), "Arr88's of different len"
return Arr88([a+b for a,b in zip(self._values, arr88._values)])

def __mul__(self, arr88):
""" Multiply two Arr88s of the same length componentwise

>>> arr88a = Arr88([1, 2, 3])
>>> arr88b = Arr88([4, 5, 6])
>>> arr88a * arr88b
Arr88([4, 10, 18])
>>> arr88a # We aren't mutating arr88a
Arr88([1, 2, 3])
>>> arr88a = Arr88(['Na', 'Batman', '!'])
>>> arr88b = Arr88([10, 1, 5])
>>> arr88a * arr88b
Arr88(['NaNaNaNaNaNaNaNaNaNa', 'Batman', '!!!!!'])
"""
# Checks that the lengths are the same
assert len(self) == len(arr88), "Arr88's of different len"
return Arr88([a*b for a,b in zip(self._values, arr88._values)])

def negate(self):
"""Negate an Arr88 with mutation

>>> arr88a = Arr88([1, 2, 3])
>>> arr88b = Arr88([4.0, -5.5, 0.0])
>>> arr88a.negate()
>>> arr88a
Arr88([-1, -2, -3])
>>> arr88b.negate()
>>> arr88b
Arr88([-4.0, 5.5, -0.0])
"""
self._values = [-a for a in self._values]``````

Use OK to test your code:

``python3 ok -q Arr88.__add__ --local``

Use OK to test your code:

``python3 ok -q Arr88.__mul__ --local``

Use OK to test your code:

``python3 ok -q Arr88.negate --local``

Complete the `apply` function that returns a new list with the function applied to every element.

``````    def apply(self, func):
""" Apply a function to an Arr88

>>> arr88a = Arr88([1, 2, 3])
>>> arr88a.apply(lambda x : x * x)
Arr88([1, 4, 9])
>>> arr88a # We aren't mutating arr88a
Arr88([1, 2, 3])
>>> arr88b = Arr88([lambda x: x, lambda x: x + 1, lambda x: x + 2])
>>> arr88b.apply(lambda f: f(1))
Arr88([1, 2, 3])
"""
return Arr88([func(a) for a in self._values])``````

Use OK to test your code:

``python3 ok -q Arr88.apply --local``

### Question 2: Checking account

We'd like to be able to cash checks, so let's add a `deposit_check` method to our `CheckingAccount` class. It will take a `Check` object as an argument, and check to see if the `payable_to` attribute matches the `CheckingAccount`'s holder. If so, it marks the `Check` as deposited, and adds the amount specified to the `CheckingAccount`'s total.

Write an appropriate `Check` class, and add the `deposit_check` method to the `CheckingAccount` class. Make sure not to copy and paste code! Use inheritance whenever possible.

See the doctests for examples of how this code should work.

``````class CheckingAccount(Account):
"""A bank account that charges for withdrawals.

>>> check = Check("Steven", 42)  # 42 dollars, payable to Steven
>>> steven_account = CheckingAccount("Steven")
>>> eric_account = CheckingAccount("Eric")
>>> eric_account.deposit_check(check)  # trying to steal steven's money
The police have been notified.
>>> eric_account.balance
0
>>> check.deposited
False
>>> steven_account.balance
0
>>> steven_account.deposit_check(check)
42
>>> check.deposited
True
>>> steven_account.deposit_check(check)  # can't cash check twice
The police have been notified.
"""
withdraw_fee = 1
interest = 0.01

def withdraw(self, amount):
return Account.withdraw(self, amount + self.withdraw_fee)

def deposit_check(self, check):
if check.payable_to != self.holder or check.deposited:
print("The police have been notified.")
else:
self.deposit(check.amount)
check.deposited = True
return self.balance

class Check(object):
def __init__(self, payable_to, amount):
self.payable_to = payable_to
self.amount = amount
self.deposited = False``````

Use OK to test your code:

``python3 ok -q CheckingAccount --local``

### Question 3: Vending Machine

Create a class called `VendingMachine` that represents a vending machine for some product. A `VendingMachine` object returns strings describing its interactions. See the doctest below for examples:

Here's a quick explanation of some of the functions you need to implement.

`restock` should update the stock and return the current stock.

`deposit` should add money to the balance and return the current balance, unless the stock is zero, then it should inform the user the stock is zero and return the money.

`vend` should either tell you how much more money needs to be deposited to buy a product, or sell you a product and return the change, or let you know the machine is out of stock.

``````class VendingMachine:
"""A vending machine that vends some product for some price.

>>> v = VendingMachine('candy', 10)
>>> v.vend()
'Machine is out of stock.'
>>> v.restock(2)
'Current candy stock: 2'
>>> v.vend()
'You must deposit \$10 more.'
>>> v.deposit(7)
'Current balance: \$7'
>>> v.vend()
'You must deposit \$3 more.'
>>> v.deposit(5)
'Current balance: \$12'
>>> v.vend()
'Here is your candy and \$2 change.'
>>> v.deposit(10)
'Current balance: \$10'
>>> v.vend()
>>> v.deposit(15)
'Machine is out of stock. Here is your \$15.'
"""
def __init__(self, product, price):
self.product = product
self.price = price
self.stock = 0
self.balance = 0

def restock(self, n):
self.stock += n
return 'Current '+self.product+' stock: '+str(self.stock)

def deposit(self, n):
if self.stock == 0:
return 'Machine is out of stock. Here is your \$'+str(n)+'.'
self.balance += n
return 'Current balance: \$'+str(self.balance)

def vend(self):
if self.stock == 0:
return 'Machine is out of stock.'
difference = self.price - self.balance
if self.balance < self.price:
return 'You must deposit \$'+str(difference)+' more.'
message = 'Here is your '+str(self.product)
if difference != 0:
message += ' and \$'+str(-difference)+' change'
self.balance = 0
self.stock -= 1
return message + '.'``````

Use OK to test your code:

``python3 ok -q VendingMachine --local``