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

### Question 1: Account

There are several things wrong with the following code! Debug the `Account` class to satisfy the docstring.

``````class Account(object):
"""A bank account that allows deposits and withdrawals.

>>> sophia_account = Account('Sophia')
>>> sophia_account.deposit(1000000)   # depositing my paycheck for the week
1000000
>>> sophia_account.transactions
[('deposit', 1000000)]
999900
>>> sophia_account.transactions
[('deposit', 1000000), ('withdraw', 100)]
"""

interest = 0.02
balance = 1000

def __init__(self, account_holder):
self.balance = 0
self.holder = account_holder
self.transactions = []

def deposit(self, amount):
"""Increase the account balance by amount and return the
new balance.
"""
self.transactions.append(('deposit', amount))
Account.balance = self.balance + amount
return self.balance

def withdraw(self, amount):
"""Decrease the account balance by amount and return the
new balance.
"""
self.transactions.append(('withdraw', amount))
if amount > self.balance:
return 'Insufficient funds'
self.balance = Account.balance - amount
return Account.balance``````

Use OK to test your code:

``python3 ok -q Account``

### Question 2: 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__``

Use OK to test your code:

``python3 ok -q Arr88.item``

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__``

Use OK to test your code:

``python3 ok -q Arr88.__mul__``

Use OK to test your code:

``python3 ok -q Arr88.negate``

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``

### 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.

Make sure your outputs match the doctest exactly!

Hint: `.format()` can help you format your strings. Here's an example:

``````>>> name = "Alex"
>>> 'My name is {}'.format(name)
'My name is Alex'``````
``````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``