Homework 7 Solutions
OOP
Question 1: Person
Modify the following Person
class to add a repeat
method, which
repeats the last thing said. If nothing has been said yet, you can
have repeat
return a string of your choosing. See the doctests for
an example of its use.
Hint: You will have to modify the
__init__
andsay
methods as well, not just therepeat
method.
class Person(object):
"""
>>> steven = Person("Steven")
>>> barb = Person("Barb")
>>> steven.say("Hello")
'Hello'
>>> steven.repeat()
'Hello'
>>> steven.greet()
'Hello, my name is Steven'
>>> barb.ask("listen to me repeat myself")
'Would you please listen to me repeat myself'
>>> barb.repeat()
'Would you please listen to me repeat myself'
>>> steven.repeat()
'Hello, my name is Steven'
"""
def __init__(self, name):
self.name = name
self.previous = "I squirreled it away before it could catch on fire."
def say(self, stuff):
self.previous = stuff
return stuff
def ask(self, stuff):
return self.say("Would you please " + stuff)
def greet(self):
return self.say("Hello, my name is " + self.name)
def repeat(self):
return self.say(self.previous)
Use OK to test your code:
python3 ok -q Person
Question 2: 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)]
>>> sophia_account.withdraw(100) # buying dinner
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 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: You can use f strings to format your strings (feel free to concatenate strings if you prefer). Here's an example:
>>> name = "Alex"
>>> f'My name is {name}'
'My name is Alex'
class VendingMachine:
"""A vending machine that vends some product for some price.
>>> v = VendingMachine('candy', 10)
>>> v.vend()
'Sold out'
>>> v.restock(2)
'Stock: 2'
>>> v.vend()
'Need $10 more'
>>> v.deposit(7)
'Current Balance: $7'
>>> v.vend()
'Need $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()
'Here is your candy'
>>> v.deposit(15)
'Sold out. 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 'Stock: {}'.format(self.stock)
def deposit(self, n):
if self.stock == 0:
return 'Sold out. Here is your ${}'.format(n)
self.balance += n
return 'Current Balance: ${}'.format(self.balance)
def vend(self):
if self.stock == 0:
return 'Sold out'
difference = self.price - self.balance
if self.balance < self.price:
return 'Need ${} more'.format(difference)
message = 'Here is your {}'.format(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
Question 4: Arr88
In this question 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 method like __init__
that allows us to call len
on our Arr88s to get their length! You can learn more about Python magic methods (aka dunder methods) here.
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 methods, like __init__
and __len__
, that allow us to use +
and *
on our Arr88s to add/multiply them element-wise!
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', ''])
>>> arr88c = arr88a + arr88b
>>> arr88c
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])
>>> arr88c = arr88a * arr88b
>>> arr88c
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])
>>> arr88c = arr88b.apply(lambda f: f(1))
>>> arr88c
Arr88([1, 2, 3])
"""
return Arr88([func(a) for a in self._values])
Use OK to test your code:
python3 ok -q Arr88.apply
Optional Questions
Question 5: Keyboard
We'd like to create a Keyboard
class that takes in an arbitrary
number of Button
s and stores these Button
s in a dictionary. The
keys in the dictionary will be strings that represent the position on the
Keyboard
, and the values will be the respective Button
. Fill out
the methods in the Keyboard
class according to each description,
using the doctests as a reference for the behavior of a Keyboard
.
class Keyboard:
"""A Keyboard takes in a list of buttons, and has a
dictionary of positions as keys, and Buttons as values.
>>> b1 = Button("button1", "H")
>>> b2 = Button("button2", "I")
>>> k = Keyboard([b1, b2])
>>> "button1" in k.buttons.keys() # Make sure to add the button to dictionary
True
>>> k.buttons["button1"].letter
'H'
>>> k.buttons["button1"].name
'button1'
>>> k.press("button1")
'H'
>>> k.press("button100")
''
>>> b1.pressed
1
>>> b2.pressed
0
>>> k.typing(["button1", "button2"])
'HI'
>>> k.typing(["button2", "button1"])
'IH'
>>> b1.pressed # make sure typing calls press!
3
>>> b2.pressed
2
"""
def __init__(self, buttons):
self.buttons = {}
for button in buttons:
self.buttons[button.name] = button
def press(self, name):
"""Takes in a name of the button pressed, and
returns that button's letter. Return an empty string
if the button does not exist. You can access the keys
of a dictionary d with d.keys(). """
if name in self.buttons.keys():
b = self.buttons[name]
b.pressed += 1
return b.letter
return ''
def typing(self, typing_input):
"""Takes in a list of names of buttons to be pressed, and
returns the total output. Make sure to call self.press"""
accumulate = ''
for name in typing_input:
accumulate+=self.press(name)
return accumulate
class Button:
def __init__(self, name, letter):
self.name = name
self.letter = letter
self.pressed = 0
Use OK to test your code:
python3 ok -q Keyboard