Lab 1: A Python Guitar Shop


Due:  Tuesday, September 11, in my office by 5:15 p.m.

Goals:

Collaboration: You should work in pairs for this lab. You may discuss the assignment with anyone you wish. You may obtain help from anyone you wish, but you should clearly document that help. You need turn in only one assignment per pair.

Contents:
Resources:

Introduction

Our overall goal in this lab is to build the final version of the GuitarShop application from OOA&D, Chapter 1, in Python.  Along the way, we'll explore some ideas in the interactive Python interpreter.

Exercises

Exercise 0: Preliminaries

Open a terminal window and change to the directory where you are keeping files for this course.  Start the interactive Python interpreter by typing python.

Open another terminal window (or tab). Use your favorite text editor to begin editing a file named guitarshop.py.

Exercise 1: Enumerated types in Python

Recall that we defined Type, Builder, and Wood as enumerated types. (Why?) Does Python have enumerated types? If not, how could you accomplish the same goal? 

In guitarshop.py, try defining a Type enumeration that includes the values ELECTRIC and ACCOUSTIC. 

Load the guitarshop module in python using "import guitarshop" and give it a try.  (Note that, when you load a module using import, you must prefix the names of classes with the module name -- e.g,. you refer to the Type class as guitarshop.Type).

We'll pause after a few minutes and talk about this as a class.

Exercise 2: Enumerated types, revisited

OK, now implement all three enumerated types: Type, Builder, and Wood. Here are the values for the enumerations:

Type

ACCOUSTIC 'accoustic'
ELECTRIC 'electric'

Builder

FENDER 'Fender'
MARTIN 'Martin'
GIBSON 'Gibson'
COLLINGS 'Collings'
OLSON 'Olson'
RYAN 'Ryan'
PRS 'PRS'
ANY 'any'

Wood

INDIAN_ROSEWOOD 'Indian rosewood'
BRAZILIAN_ROSEWOOD 'Brazilian rosewood'
MAHOGANY 'mahogany'
MAPLE 'maple'
COCOBOLO 'cocobolo'
CEDAR 'cedar'
ADIRONDACK 'adirondack'
ALDER 'alder'
SITKA 'sitka spruce'

You can reload the guitarshop module in python using reload(guitarshop).

Exercise 3: GuitarSpec class diagram

OOA&D, p. 39, shows the latest class diagram for GuitarSpec. But it's missing some things we added at the end of the chapter: the numStrings property and the matches(otherSpec) method. 

Draw a new class diagram for GuitarSpec that includes these methods. You'll refer to it in the next exercise.

Exercise 4: GuitarSpec properties

In guitarshop.py, create a GuitarSpec class. Define its __init__ method and get methods for all the properties.

Reload the guitarshop module in python using reload(guitarshop) and give your new class a try!

Exercise 5: Matching GuitarSpecs

Now, onto that new matches(otherSpec) method... We need to verify that each of the properties of self and otherSpec match. For builder, type, backWood, and topWood, it's easy: these are all enumerated types, and we can check their equality with the == and != operators. numStrings is a number, so it's easy too.

What about model? That could be any string. The Java code uses the following test:

((model != null) && !(model.equals("")) && (!model.equals(otherSpec.model))) [OOA&D, p. 45]
  1. Write this expression in Python. (Note that in Python, the name for the null reference is None.)
  2. The test above leaves out something important: "stratocastor" and "Stratocastor" will not match! How can we compare two strings, ignoring case, in Python? Rewrite your expression so that it will return true even if the two strings have different cases. (Hint: Use one of our references to look up what methods the built-in String type has, or type help(str) in the Python interpreter.)
  3. Now, implement the GuitarSpec.matches(otherSpec) method.
  4. Reload the guitarshop module in python, create two new GuitarSpec objects, and compare them using matches.

Exercise 6: The Guitar class

Since we encapsulated most of a guitar's properties into the GuitarSpec class, the Guitar class is very simple! The class diagram on p. 38 of OOA&D is still correct. Implement the Guitar class.

Exercise 7: The Inventory class

As we can see in the class diagram on p. 39 of OOA&D, the Inventory class has one property, a list of guitars, and three methods: addGuitar(...) , getGuitar(serialNumber), and search(guitarSpec). You should have everything you need now to implement the Inventory class. Believe it or not, you won't need to write very much code. Go to it!

Exercise 8: Testing, Testing, Testing

Hmm, we've been leaving out something important. We've been doing some little tests as we go along, but do the Guitar and Inventory classes work? And what if we decide to change something in GuitarSpec---how do we know that still works? It would be helpful to have some automated tests. Luckily, the authors of OOA&D already wrote some, so we'll just translate them into Python.

  1. I'm going to give you most of the test code because test code is, frankly, usually pretty boring.  Copy and paste the following into the bottom of guitarshop.py.
    def createTestInventory():
    "Create an inventory of guitars for testing this module."
    inventory = Inventory()
    inventory.addGuitar(Guitar('45790J', 1799.95,
    GuitarSpec(Builder.FENDER,
    'Stratocastor',
    Type.ELECTRIC,
    6,
    Wood.ALDER,
    Wood.ALDER)))
    inventory.addGuitar(Guitar('23578Q', 1099.95,
    GuitarSpec(Builder.FENDER,
    'Fictocastor',
    Type.ELECTRIC,
    6,
    Wood.ALDER,
    Wood.ALDER)))
    inventory.addGuitar(Guitar('34789A', 1499.95,
    GuitarSpec(Builder.FENDER,
    'Stratocastor',
    Type.ELECTRIC,
    6,
    Wood.ALDER,
    Wood.ALDER)))
    inventory.addGuitar(Guitar('83959B', 1299.95,
    GuitarSpec(Builder.MARTIN,
    'JM',
    Type.ACCOUSTIC,
    6,
    Wood.SITKA,
    Wood.MAHOGANY)))
    return inventory

    if __name__ == '__main__':
    inventory = createTestInventory()
    whatErinLikes = GuitarSpec(Builder.FENDER, 'stratocastor', Type.ELECTRIC,
    6, Wood.ALDER, Wood.ALDER)
    matchingGuitars = inventory.search(whatErinLikes)
    if len(matchingGuitars) > 0:
    print 'Erin, you might like these guitars:'
    # TODO: print out info for each matching guitar
    else:
    print 'Sorry, Erin, we have nothing for you.'
  2. Open a third terminal window (or tab) and type run python guitarshop.py. If all goes well, you'll see the following output:
    Erin, you might like these guitars: 
    Well, that's kind of helpful. We know some guitars matched, but it didn't tell us which ones.
  3. Where the code above says TODO, fill in your own code to iterate over matching guitars and print some information about each one. Remember that you can use string formatting (see PPR, p. 14-15) to make your job easier. The output should look something like the following:

    Erin, you might like these guitars:
    We have a 6-string Fender Stratocastor Electric guitar:
    Alder back and sides, Alder top.
    You can have it for only $1799.95!
    ----
    We have a 6-string Fender Stratocastor Electric guitar:
    Alder back and sides, Alder top.
    You can have it for only $1499.95!
    ----

Exercise 9: Higher-order procedures in Python

Just like Scheme, Python lets you write higher-order procedures. (This is one of the great things about Python---it incorporates many of the best features of the imperative, functional, and object-oriented paradigms.) In particular, Python has has the map and filter procedures already defined.
  1. In the interpreter, define a new procedure, double(x), that returns 2*x. Then write an expression using map to double each item in the list [1,2,3].

  2. We also have anonymous procedures! Look up "lambda expressions" in one of our references. Then write an expression that uses map to double each item in the list [1,2,3] without using the double procedure.
  3. Now, we'll combine HOP and OOP. Write an expression using map that converts each string in the list ['spam', 'eggs'] to ALL UPPER CASE.
  4. filter is a standard higher-order procedure that lets us pick out items from a list that agree with some predicate. What do you think the following expression will do?
    filter(lambda i: i%10 == 0, [0, 5, 10, 15, 20, 25, 30])
    Try it out. Did it do what you expected?
  5. Reimplement the search method of Inventory to use filter rather than a for loop. Put those automated tests to work by running python guitarshop.py again---make sure that the code still works.

Exercise 10: Advantages & disadvantages

Whew! Good work! You now know many of Python's features. For this final exercise, a couple of questions: 

  1. For this application (or in general), what is at least one advantage of Python compared to Java?
  2. For this application (or in general), what is at least one disadvantage of Python compared to Java?

Extra credit

For extra credit, think of a way to refine the guitarshop code or tests that we didn't talk about in class. Implement this refinement (while ensuring your code still works correctly!) and tell me why it is better.

Turning in your work

Capture your source code and its output in a single file by running the following commands:

script groupname.txt
cat guitarshop.py
python guitarshop.py
<CTRL-D>

To print the file, type 

enscript -G -Pprintername groupname.txt

Turn in this paper printout to me along with your answers to the questions in the lab; you can slide it under my door if I'm not there. 

Also email me a copy of your source code. Please put your code in the body of the message and put "CSC223-Lab-1" in the subject line.


Janet Davis (davisjan@cs.grinnell.edu)

Created August 14, 2007
Last revised September 7, 2007