This laboratory session asks you to write a few classes and experiment with some existing code. Of course, the problem with existing code is that it must be existing. To work with the set class in Exercise 5, either pull down the class source code off the Internet (see the Preface) or type it up by hand (it’s fairly small). These programs are starting to get more sophisticated, so be sure to check the solutions at the end of the book for pointers. If you’re pressed for time, we suspect that the last exercise dealing with composition will probably be the most fun of the bunch (of course, we already know the answers).
The basics. Write a class called
Adder
that exports a method add(self, x, y)
that prints a “Not Implemented” message.
Then define two subclasses of Adder
that implement
the add
method:
ListAdder
, with an add
method that returns the concatenation of its two list arguments
DictAdder
, with an add
method that returns a new dictionary with the items in both its two dictionary arguments (any definition of addition will do)
Experiment by making instances of all three of your classes
interactively and calling their add
methods.
Finally, extend your classes to save an object in a constructor (a
list or a dictionary) and overload the + operator to replace the
add
method. Where is the best place to put the
constructors and operator overload methods (i.e., in which classes)?
What sorts of objects can you add to your class instances?
Operator
overloading. Write a class called
Mylist
that “wraps” a Python list: it
should overload most list operators and operations—+, indexing,
iteration, slicing, and list methods such as
append
and sort
. See the Python
reference manual for a list of all possible methods to overload. Also
provide a constructor for your class that takes an existing list (or
a Mylist
instance) and copies its components into
an instance member. Experiment with your class interactively. Things
to explore:
Why is copying the initial value important here?
Can you use an
empty slice (e.g., start[:]
) to copy the initial
value if it’s a Mylist
instance?
Is there a general way to route list method calls to the wrapped list?
Can you add a
Mylist
and a regular list? How about a list and a
Mylist
instance?
What type of object should operations like + and slicing return; how about indexing?
Subclassing. Now, make a subclass of
Mylist
from Exercise 2 called
MylistSub
, which extends Mylist
to print a message to stdout
before each
overloaded operation is called and counts the number of calls.
MylistSub
should inherit basic method behavior
from Mylist
. For instance, adding a sequence to a
MylistSub
should print a message, increment the
counter for + calls, and perform the superclass’s method. Also
introduce a new method that displays the operation counters to
stdout
and experiment with your class
interactively. Do your counters count calls per instance, or per
class (for all instances of the class)? How would you program both of
these? (Hint: it depends on which object the count members are
assigned to: class members are shared by instances,
self
members are per-instance data.)
Metaclass methods.
Write a class called Meta
with methods that
intercept every attribute qualification (both fetches and
assignments) and prints a message with their arguments to
stdout
. Create a Meta
instance
and experiment with qualifying it interactively. What happens when
you try to use the instance in expressions? Try adding, indexing, and
slicing the instance of your class.
Set objects. Experiment with the set class described in this chapter (from Section 6.8.5). Run commands to do the following sorts of operations:
Create two sets of integers, and
compute their intersection and union by using
&
and |
operator
expressions.
Create a set from a string, and experiment with indexing your set; which methods in the class are called?
Try iterating through the items in
your string set using a for
loop; which methods
run this time?
Try computing the intersection and union of your string set and a simple Python string; does it work?
Now, extend your set by
subclassing to handle arbitrarily many operands using a
*args
argument form (hint: see the function
versions of these algorithms in Chapter 4).
Compute intersections and unions of multiple operands with your set
subclass. How can you intersect three or more sets, given that
&
has only two sides?
How would you go about emulating other list operations in the
set class? (Hints:
__ add
_
_
can catch concatenation, and
__ getattr
__
can
pass most list method calls off to the wrapped list.)
Class tree
links. In a footnote in the section on multiple
inheritance, we mentioned that classes have a
_
_ bases
__
attribute that
returns a tuple of the class’s superclass objects (the ones in
parentheses in the class header). Use
_
_ bases
__
to extend the
Lister
mixin class, so that it prints the names of
the immediate superclasses of the instance’s class too. When
you’re done, the first line of the string representation should
look like this:
<Instance of Sub(Super, Lister), address 7841200:.
How would you go about listing class attributes too?
Composition. Simulate a fast-food ordering scenario by defining four classes:
Lunch
: a
container and controller class
Customer
: the actor that buys food
Employee
: the actor that a customer
orders from
Food
: what the
customer buys
To get you started, here are the classes and methods you’ll be defining:
class Lunch: def __init__(self) # make/embed Customer and Employee def order(self, foodName) # start a Customer order simulation def result(self) # ask the Customer what kind of Food it has class Customer: def __init__(self) # initialize my food to None def placeOrder(self, foodName, employee) # place order with an Employee def printFood(self) # print the name of my food class Employee: def takeOrder(self, foodName) # return a Food, with requested name class Food: def __init__(self, name) # store food name
The order simulation works as follows:
The Lunch
class’s constructor should make and embed an instance of Customer
and Employee
, and export a method called order
. When called, this order
method should ask the Customer
to place an order, by calling its placeOrder
method. The Customer
’s placeOrder
method should in turn ask the Employee
object for a new Food
object, by calling the Employee
’s takeOrder
method.
Food
objects should store a food name string (e.g., "burritos"
), passed down from Lunch.order
to Customer.placeOrder
, to Employee.takeOrder
, and finally to Food
’s constructor. The top-level Lunch
class should also export a method called result
, which asks the customer to print the name of the food it received from the Employee
(this can be used to test your simulation).
Note that Lunch
needs to either pass the Employee
to the Customer
, or pass itself to the Customer
, in order to allow the Customer
to call Employee
methods.
Experiment with your classes interactively by importing the
Lunch
class, calling its order
method to run an interaction, and then calling its
result
method to verify that the
Customer
got what he or she ordered. In this
simulation, the Customer
is the active agent; how
would your classes change if Employee
were the
object that initiated customer/employee interaction instead?