Instructions

Download hw07.zip. Inside the archive, you will find starter files for the questions in this homework, along with a copy of the OK autograder.

Readings: This homework relies on following references:

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__ and say methods as well, not just the repeat 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
        "*** YOUR CODE HERE ***"

    def say(self, stuff):
        "*** YOUR CODE HERE ***"
        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):
        "*** YOUR CODE HERE ***"

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

    "*** YOUR CODE HERE ***"

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
        """
        "*** YOUR CODE HERE ***"
        

    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
        """
        "*** YOUR CODE HERE ***"
        

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"
        "*** YOUR CODE HERE ***"
        

    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"
        "*** YOUR CODE HERE ***"
        

    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])
        """
        "*** YOUR CODE HERE ***"
        

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])
        """
        "*** YOUR CODE HERE ***"
        

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 Buttons and stores these Buttons 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 = {}
        "*** YOUR CODE HERE ***"

    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(). """
        "*** YOUR CODE HERE ***"

    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"""
        "*** YOUR CODE HERE ***"

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

Submission

When you are done, submit your file to Gradescope. You only need to upload the following files:

  • hw07.py
  • hw07_extra.py
You may submit more than once before the deadline; only the final submission will be graded. It is your responsibility to check that the autograder on Gradescope runs as expected after you upload your submission.