Lab 8: Inheritance + Linked Lists
Due at 11:59:59 pm on 4/2/2024.
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:
- Implement the
Check
class's__init__
andmark_deposited
methods, which represents a check that can be deposited into an account. Read the doctests in theCheck
class to learn about the behavior of these methods. Hint: Think about what sort of attributes ourCheck
class should have. Implement the
CheckingAccount
class'sdeposit_check
method. It will take aCheck
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
- If the check was already deposited, print
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