Lab 6: Object-Oriented Programming and Inheritance

Table of Contents

Submission

This lab is due at 11:59pm on 10/15/2014.

Please start at the beginning of the lab and work your way through, working and talking over Python's behavior in the conceptual questions with your classmates nearby. These questions are designed to help you experiment with the concepts on the Python interpreter. They are good tests for your understanding.

When you are done with lab, submit Questions 2 and 5 (provided in the starter file lab06.py) to receive credit for this lab. The rest (6, 7) are questions that are considered extra practice — they can be found in the the lab06_extra.py file. It is recommended that you complete these problems on your own time.

By the end of this lab, you should have submitted the lab06 assignment using the command submit lab06. You can check if we received your submission by running glookup -t. Click here to see more complete submission instructions.

Object-Oriented Programming

Introduction

Object-oriented programming (OOP) is a style of programming that allows you to think of code in terms of "objects." Here's an example of a Car class:

class Car(object):
    num_wheels = 4

    def __init__(self, color):
        self.wheels = Car.num_wheels
        self.color = color

    def drive(self):
        if self.wheels <= Car.num_wheels:
            return self.color + ' car cannot drive!'
        return self.color + ' car goes vroom!'

    def pop_tire(self):
        if self.wheels > 0:
            self.wheels -= 1

Here's some terminology:

Types of variables

When dealing with OOP, there are three types of variables you should be aware of:

Question 1

Predict the result of evaluating the following calls in the interpreter. Then try them out yourself!

>>> class Account(object):
...     interest = 0.02
...     def __init__(self, account_holder):
...         self.balance = 0
...         self.holder = account_holder
...     def deposit(self, amount):
...         self.balance = self.balance + amount
...         print("Yes!")
...
>>> a = Account("Billy")
>>> a.account_holder
______
AttributeError: 'Account' object has no attribute 'account_holder'
>>> a.holder
______
'Billy'
>>> Account.holder
______
AttributeError: type object 'Account' has no attribute 'holder'
>>> Account.interest
______
0.02
>>> a.interest
______
0.02
>>> Account.interest = 0.03 >>> a.interest
______
0.03
>>> a.deposit(1000)
______
Yes!
>>> a.balance
______
1000
>>> a.interest = 9001 >>> Account.interest
______
0.03

Question 2

Modify the following Person class to add a repeat method, which repeats the last thing said. See the doctests for an example of its use.

Hint: you will have to modify other methods as well, not just the repeat method.

class Person(object):
    """Person class.

    >>> steven = Person("Steven")
    >>> steven.repeat()       # starts at whatever value you'd like
    'I squirreled it away before it could catch on fire.'
    >>> steven.say("Hello")
    'Hello'
    >>> steven.repeat()
    'Hello'
    >>> steven.greet()
    'Hello, my name is Steven'
    >>> steven.repeat()
    'Hello, my name is Steven'
    >>> steven.ask("preserve abstraction barriers")
    'Would you please preserve abstraction barriers'
    >>> steven.repeat()
    'Would you please preserve abstraction barriers'
    """
    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):
"*** YOUR CODE HERE ***"
return self.say(self.previous)

Inheritance

Question 3

Predict the result of evaluating the following calls in the interpreter. Then try them out yourself!

>>> class Account(object):
...     interest = 0.02
...     def __init__(self, account_holder):
...         self.balance = 0
...         self.holder = account_holder
...     def deposit(self, amount):
...         self.balance = self.balance + amount
...         print("Yes!")
...
>>> class CheckingAccount(Account):
...     def __init__(self, account_holder):
...         Account.__init__(self, account_holder)
...     def deposit(self, amount):
...         Account.deposit(self, amount)
...         print("Have a nice day!")
...
>>> a = Account("Billy")
>>> a.balance
______
0
>>> c = CheckingAccount("Eric") >>> c.balance
______
0
>>> a.deposit(30)
______
Yes!
>>> c.deposit(30)
______
Yes! Have a nice day!
>>> c.interest
______
0.02

Question 4

Suppose now that we wanted to define a class called DoubleTalker to represent people who always say things twice:

>>> steven = DoubleTalker("Steven")
>>> steven.say("hello")
"hello hello"
>>> steven.say("the sky is falling")
"the sky is falling the sky is falling"

Consider the following three definitions for DoubleTalker that inherit from the Person class:

class DoubleTalker(Person):
    def __init__(self, name):
        Person.__init__(self, name)
    def say(self, stuff):
        return Person.say(self, stuff) + " " + self.repeat()

class DoubleTalker(Person):
    def __init__(self, name):
        Person.__init__(self, name)
    def say(self, stuff):
        return stuff + " " + stuff

class DoubleTalker(Person):
    def __init__(self, name):
        Person.__init__(self, name)
    def say(self, stuff):
        return Person.say(self, stuff + " " + stuff)

Determine which of these definitions work as intended. Also determine for which of the methods the three versions would respond differently. (Don't forget about the repeat method!)

The last one works as intended. For the first and second ones, calling repeat would fail.

Question 5

Modify the Account class from lecture so that it has a new attribute, transactions, that is a list keeping track of any transactions performed. See the doctest for an example.

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)]
    >>> 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

Extra Questions

The following questions are for extra practice — they can be found in the the lab06_extra.py file. It is recommended that you complete these problems as well, but you do not need to turn them in for credit.

Question 6

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.

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):
"*** YOUR CODE HERE ***"
def __init__(self, payable_to, amount): self.payable_to = payable_to self.amount = amount self.deposited = False

Question 7

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 ints that represent the postition 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 an arbitrary amount of buttons, and has a
    dictionary of positions as keys, and values as Buttons.

    >>> b1 = Button(0, "H")
    >>> b2 = Button(1, "I")
    >>> k = Keyboard(b1, b2)
    >>> k.buttons[0].key
    'H'
    >>> k.press(1)
    'I'
    >>> k.typing([0, 1])
    'HI'
    >>> k.typing([1, 0])
    'IH'
    >>> b1.pressed
    2
    >>> b2.pressed
    3
    """

    def __init__(self, *args):
"*** YOUR CODE HERE ***"
self.buttons = {} for button in args: self.buttons[button.pos] = button
def press(self, info): """Takes in a position of the button pressed, and returns that button's output"""
"*** YOUR CODE HERE ***"
if info in self.buttons.keys(): b = self.buttons[info] b.pressed += 1 return b.key return ''
def typing(self, typing_input): """Takes in a list of positions of buttons pressed, and returns the total output"""
"*** YOUR CODE HERE ***"
accumulate = '' for pos in typing_input: accumulate+=self.press(pos) return accumulate
class Button: def __init__(self, pos, key): self.pos = pos self.key = key self.pressed = 0