## Starter Files

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

## Inheritance

### Question 1: Checking account

Consider the `Account` class provided below, which represents the attributes and behaviors of a bank account.

``````class Account:
"""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)]
>>> eric_account.withdraw(100)      # buying dinner
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``````

We want to create a new class called `CheckingAccount` which inherits from `Account`. We'd like to be able to cash checks to our `CheckingAccount`. To do this, you will have to implement the following:

1. Implement the `Check` class's `__init__` and `mark_deposited` methods, which represents a check that can be deposited into an account. Read the doctests in the `Check` class to learn about the behavior of these methods. Hint: Think about what sort of attributes our `Check` class should have.
2. Implement the `CheckingAccount` class's `deposit_check` method. It will take a `Check` object as an argument.

• If the check was already deposited, print `The police have been notified`
• If the check is NOT payable to the checking account holder, print `The police have been notified`
• Otherwise, mark the check as deposited and deposit the amount into the checking account

The doctests for this problem are extremely important and valuable in understanding how this code works and how the `CheckingAccount` and `Check` classes should interact with each other.

``````class Check:
"""A Check object that can be deposited.

>>> eric_check = Check("Eric", 30)  # 30 dollars, payable to Eric
>>> eric_check.payable_to
'Eric'
>>> eric_check.amount
30
>>> eric_check.deposited
False
>>> eric_check.mark_deposited()
>>> eric_check.deposited
True
"""
def __init__(self, payable_to, amount):
"*** YOUR CODE HERE ***"
self.payable_to = payable_to
self.amount = amount
self.deposited = False
def mark_deposited(self):
"*** YOUR CODE HERE ***"
self.deposited = True
class CheckingAccount(Account):
"""A bank account that charges for withdrawals and can deposit checks.

>>> 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
>>> steven_account.balance
0
>>> check.deposited
False
>>> eric_account.transactions
[]
>>> steven_account.transactions
[]
>>> steven_account.deposit_check(check)
42
>>> check.deposited
True
>>> steven_account.transactions
[('deposit', 42)]
>>> steven_account.deposit_check(check)  # can't cash check twice
The police have been notified.
>>> steven_account.transactions  # transactions history shouldn't have changed
[('deposit', 42)]
"""
withdraw_fee = 1
interest = 0.01

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

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

Use OK to test your code:

``python3 ok -q CheckingAccount``

## Linked Lists

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 = Link(1, Link(2, Link(3)))
>>> s
Link(1, Link(2, Link(3)))
"""
empty = ()

def __init__(self, first, rest=empty):
assert rest is Link.empty or isinstance(rest, Link)
self.first = first
self.rest = rest

def __repr__(self):
if self.rest is not Link.empty:
rest_str = ', ' + repr(self.rest)
else:
rest_str = ''
return 'Link({0}{1})'.format(repr(self.first), 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):
if link is Link.empty:
print('This linked list is empty!')
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 2: 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 *
>>> link = Link(1, Link(2, Link(3)))
>>> link.first
______1
>>> link.rest.first
______2
>>> link.rest.rest.rest is Link.empty
______True
>>> link.first = 9001
>>> link.first
______9001
>>> link.rest = link.rest.rest
>>> link.rest.first
______3
>>> link = Link(1)
>>> link.rest = link
>>> link.rest.rest.rest.rest.first
______1
>>> link = Link(2, Link(3, Link(4)))
>>> link2 = Link(1, link)
>>> link2.first
______1
>>> link2.rest.first
______2
>>> print_link(link2) # Look at print_link in lab08.py
______<1 2 3 4>``````

### Question 3: 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.

>>> link = list_to_link([1, 2, 3])
>>> print_link(link)
<1 2 3>
>>> print_link(list_to_link([4]))
<4>
"""
"*** YOUR CODE HERE ***"
if not lst:
return Link.empty
else:
return Link(lst[0], list_to_link(lst[1:]))``````

Use OK to test your code:

``python3 ok -q list_to_link``

### Question 4: Reverse

Implement `reverse`, which takes a linked list `link` and returns a linked list containing the elements of `link` in reverse order. The original `link` should be unchanged.

``````def reverse(link):
"""Returns a Link that is the reverse of the original.

>>> print_link(reverse(Link(1)))
<1>
>>> link = Link(1, Link(2, Link(3)))
>>> new = reverse(link)
>>> print_link(new)
<3 2 1>
>>> print_link(link)
<1 2 3>
"""
"*** YOUR CODE HERE ***"
#new = Link(link.first)
#while link.rest is not Link.empty:
#    link = link.rest
#    new = Link(link.first, new)
#return new

new = Link.empty
while link is not Link.empty:
new = Link(link.first, new)
link = link.rest
return new

# Recursive solution
def reverse(link):
def reverse_to(link, t):
if link is Link.empty:
return t
else:
return reverse_to(link.rest, Link(link.first, t))
return reverse_to(link, Link.empty)``````

Use OK to test your code:

``python3 ok -q reverse``

### Question 5: Slice

Implement a function `slice_link` that slices a given `link`. `slice_link` should slice the `link` starting at `start` and ending one element before `end`, as with slicing a normal Python list.

``````def slice_link(link, start, end):
"""Slices a Link from start to end (as with a normal Python list).

>>> link = Link(3, Link(1, Link(4, Link(1, Link(5, Link(9))))))
>>> new = slice_link(link, 1, 4)
>>> new
Link(1, Link(4, Link(1)))
>>> link2 = slice_link(Link(1), 0, 1)
>>> link2
Link(1)
>>> link3 = slice_link(Link.empty, 0, 0)
>>> link3
()
"""
"*** YOUR CODE HERE ***"
if end == 0:
return Link.empty
elif start == 0:
return Link(link.first, slice_link(link.rest, 0, end-1))
else:
return slice_link(link.rest, start-1, end-1)``````

Use OK to test your code:

``python3 ok -q slice_link``

## Submission

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

• `lab08.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.