## Submission Note

The last problem is optional. If you choose not to do this problem, the Gradescope autograder may complain that you have not submitted the `lab08_extra.py` file. This is fine, as our autograder does not grade this question.

## Inheritance

### Question 1: Mint

Complete the `Mint` and `Coin` classes so that the coins created by a mint have the correct year and worth.

• Each `Mint` instance has a `year` stamp. The `update` method sets the `year` stamp to the `current_year` class attribute of the `Mint` class.
• The `create` method takes a subclass of `Coin` and returns an instance of that class stamped with the `mint`'s year (which may be different from `Mint.current_year` if it has not been updated.) Hint: Check out the `create_animal` method in this demo.
• A `Coin`'s `worth` method returns the `cents` value of the coin plus one extra cent for each year of age beyond 50. A coin's age can be determined by subtracting the coin's year from the `current_year` class attribute of the `Mint` class.
``````class Mint:
"""A mint creates coins by stamping on years.

The update method sets the mint's stamp to Mint.current_year.

>>> mint = Mint()
>>> mint.year
2020
>>> dime = mint.create(Dime)
>>> dime.year
2020
>>> Mint.current_year = 2100  # Time passes
>>> nickel = mint.create(Nickel)
>>> nickel.year     # The mint has not updated its stamp yet
2020
>>> nickel.worth()  # 5 cents + (80 - 50 years)
35
>>> mint.update()   # The mint's year is updated to 2100
>>> Mint.current_year = 2175     # More time passes
>>> mint.create(Dime).worth()    # 10 cents + (75 - 50 years)
35
>>> Mint().create(Dime).worth()  # A new mint has the current year
10
>>> dime.worth()     # 10 cents + (155 - 50 years). 155 is because this dime instance has year 2020
115
>>> Dime.cents = 20  # Upgrade all dimes!
>>> dime.worth()     # 20 cents + (155 - 50 years)
125
>>> m = Mint()
>>> n = m.create(Nickel)
>>> n.worth()
5
>>> n.year = 2015
>>> n.worth() # 5 cents + (160 - 50 years). 160 is from 2175 - 2015
115
"""
current_year = 2020

def __init__(self):
self.update()

def create(self, coin):
return coin(self.year)
def update(self):
self.year = Mint.current_year
class Coin:
cents = 0 # Default value of 0. This value will be overridden in subclasses.

def __init__(self, year):
self.year = year

def worth(self):
"The worth is a coin's face value + 1 cent for each year over age 50."
return self.cents + max(0, Mint.current_year - self.year - 50)
class Nickel(Coin):
cents = 5

class Dime(Coin):
cents = 10``````

Use OK to test your code:

``python3 ok -q Mint``

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

The `Account` class has been provided.

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

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

interest = 0.02

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))
self.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 = self.balance - amount
return self.balance

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

A linked list is either an empty linked list (`Link.empty`) or a first value and the rest of the linked list.

``````class Link:
"""
>>> s
"""
empty = ()

def __init__(self, first, rest=empty):
self.first = first
self.rest = rest

def __repr__(self):
rest_str = ', ' + repr(self.rest)
else:
rest_str = ''

To check if a `Link` is empty, compare it against the class attribute `Link.empty`. For example, the below function prints out whether or not the link it is handed is empty:

``````def test_empty(link):
else:
print('This linked list is not empty!')``````

Note: Linked lists are recursive data structures! A linked list contains the first element of the list (`first`) and a reference to another linked list (`rest`) which contains the rest of the values in the list.

### Question 3: WWPP: Linked Lists

Use OK to test your knowledge with the following "What Would Python Print?" questions:

``python3 ok -q link -u``

If you get stuck, try loading lab08.py into an interpreter or drawing out the diagram for the linked list on a piece of paper.

``````>>> from lab08 import *
______1
______2
______True
______9001
______3
______1
______1
______2
______<1 2 3 4>``````

### Question 4: List to Link

Write a function `list_to_link` that converts a Python list to a `Link`.

``````def list_to_link(lst):
"""Takes a Python list and returns a Link with the same elements.

<1 2 3>
<4>
"""
if not lst:
else:

Use OK to test your code:

``python3 ok -q list_to_link``

Let's implement a method in order to add together items of `link1` and `link2` such that the new linked list contains all of the elements of `link1`, then all of the elements of `link2`. Do not assume that the links are the same length.

``````def add_links(link1, link2):

<1 2 3 4 5>
<10 20 30>
"""
else:

# Iterative version (using reverse)
return reverse(result)``````

Use OK to test your code:

``python3 ok -q add_links``

## Optional Question

### Question 6: Rotate

Implement `rotate`, which takes a linked list `link` and a number `n`, and returns a linked list containing the elements of `link` shifted to the right `n` times. Elements at the end are shifted back to the beginning of the linked list. The value of `n` may be greater than the length of the linked list, in which case the values should continue to be rotated.

``````def rotate(link, n):
"""Rotates the Link so that its elements are shifted to the right n times.

"""
length = 0
while ptr:
ptr = ptr.rest
length += 1
n %= length
``python3 ok -q rotate``