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 commandsubmit lab06
. You can check if we received your submission by runningglookup -t
. Click here to see more complete submission instructions.
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:
Car
class (shown above) describes the behavior and data that
all Car
objects have.instance: a particular occurrence of a class. In Python, we create instances of a class like this:
>>> my_car = Car('red')
my_car
is an instance of the Car
class.
attribute or field: a variable that belongs to the class.
Think of an attribute as a quality of the object: cars have wheels
and color, so we have given our Car
class self.wheels
and
self.color
attributes. We can access attributes using dot
notation:
>>> my_car.color
'red'
>>> my_car.wheels
4
method: Methods are just like normal functions, except that they
are tied to an instance or a class. Think of a method as a "verb" of
the class: cars can drive and also pop their tires, so we have
given our Car
class the methods drive
and pop_tire
. We call
methods using dot notation:
>>> my_car = Car('red')
>>> my_car.drive()
'red car goes vroom!'
constructor: As with data abstraction, constructors describe how
to build an instance of the class. Most classes have a constructor.
In Python, the constructor of the class defined as __init__
. For
example, here is the Car
class's constructor:
def __init__(self, color):
self.wheels = Car.num_wheels
self.color = color
The constructor takes in one argument, color
. As you can see, the
constructor also creates the self.wheels
and self.color
attributes.
self
: in Python, self
is the first parameter for many methods
(in this class, we will only use methods whose first parameter is
self
). When a method is called, self
is bound to an instance of
the class. For example:
>>> my_car = Car('red')
>>> car.drive()
Notice that the drive
method takes in self
as an argument, but it
looks like we didn't pass one in! This is because the dot notation
implicitly passes in car
as self
for us.
When dealing with OOP, there are three types of variables you should be aware of:
color
variable in the __init__
method is a local variable (not
the self.color
variable).instance attribute: Unlike local variables, instance attributes
will still be accessible after method calls have finished. Each
instance of a class keeps its own version of the instance attribute
— for example, we might have two Car
objects, where one's
self.color
is red, and the other's self.color
is blue.
>>> car1 = Car('red')
>>> car2 = Car('blue')
>>> car1.color
'red'
>>> car2.color
'blue'
>>> car1.color = 'yellow'
>>> car1.color
'yellow'
>>> car2.color
'blue'
class attribute: As with instance attributes, class attributes
also persist across method calls. However, unlike instance
attributes, all instances of a class will share the same class
attributes. For example, num_wheels
is a class attribute of the
Car
class.
>>> car1 = Car('red')
>>> car2 = Car('blue')
>>> car1.num_wheels
4
>>> car2.num_wheels
4
>>> Car.num_wheels = 2
>>> car1.num_wheels
2
>>> car2.num_wheels
2
Notice that we can access class attributes by saying <class
name>.<attribute>
, such as Car.num_wheels
, or by saying
<instance>.<attribute>
, such as car1.num_wheels
.
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
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)
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
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.
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
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.
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
We'd like to create a Keyboard
class that takes in an arbitrary
number of Button
s and stores these Button
s 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