CS9H Online Study Guide

Dan Garcia, with lots of help from Ka-Ping Yee and Mike Clancy
Last Updated : 2013-01-31

Table of Contents

Introduction

In CS9H, you learn to program in Python. Course material consists of quizzes, which test your knowledge of language and low-level conceptual details, and programming assignments, which exercise your overall command of the language. This volume supplies a framework for the course. It contains the following:

Study guides. Each study guide focuses on a particular programming topic. It provides references to textbook material describing the topic, and suggests exercises for self-study. The study guides reference the following text, available online.

How to Think Like a Computer Scientist: Learning with Python, by Allen Downey, Jeff Elkner and Chris Meyers (Green Tea Press, 2004; ISBN 0971677506). You will be able to access it online.

In addition, there are a supplementary set of online notes which are listed (and linked) in the reading sections below.

Programming assignments. Each one has a header page (this tells you the title and related topics) that is followed by the actual assignment.

System information. Some programming assignments require you to use features of Python not adequately described in the standard documents. More information is included with the corresponding study guide.

Sample quiz questions, with solutions. These help you prepare for the quizzes.

Note: This course is written for Python 2. Python 3 may be used, but be aware that all sample code and quizzes are written in Python 2 and that versions 2 and 3 are incompatible.

What's special about Python?

Language

Built-in types

Sequence handling

Flexible functions

Code reuse

Library

Structure of quizzes and programs in CS9H

The following table outlines the relationship between quizzes and programs. All the material for a particular grouping should be completed before material in the next grouping; however, programs within a group must be done before the quiz for that group.

group programs quizzes
A Cellular Automaton Expressions and Functions
B Python-powered unit conversion
Basic web programming
Collections & Mutation
C Online Mad Libs® Input and Output
D Turtle Behavior Object-oriented programming
E Turtle Arena Tkinter and Event-Based Programming

For information about deadlines, consult the Information and Rules document (available at the Self-paced center).

Program - Cellular Automaton

This program introduces you to Python by giving you practice with the interpreter, expressions, strings, functions and the various flow-of-control constructs.

Related quizzes

Programming assignment

Readings

Quiz - Expressions and Functions

You learn the "art" of using Python not only by writing programs of your own, but also by reading those of others. Quiz questions ask you to generate code as well as understand, debug, and criticize already-written code. There is a lot of reading for this first topic, so get started right away. It will cover most of the Python syntax you will learn:

Readings

Sample Quiz

  1. What result (or error) would be yielded by each of the following Python expressions?
  2. -2 ** 3 + 7
    -3 % 5 - 8
    'spam' * 3 + 'lovely spam'
    'spam' + 3 + 'lovely spam'
    'eggs' and '' and 5
    0 or 4 and 2 or 7
    

    Answers:

    >>> -2 ** 3 + 7
    -1
    >>> -3 % 5 - 8
    -6
    >>> 'spam' * 3 + 'lovely spam'
    'spamspamspamlovely spam'
    >>> 'spam' + 3 + 'lovely spam'
    TypeError: cannot concatenate 'str' and 'int' objects
    >>> 'eggs' and '' and 5
    ''
    >>> 0 or 4 and 2 or 7
    2
    
  1. Write two versions of a Python function named mystery that has the following behaviour. In the first version, use a loop to construct the result. In the second version, do not use any loops.
  2. >>> mystery(15, 'red')
    'redredredredred'
    >>> mystery(12, 'green')
    'greengreengr'
    >>> mystery(26, 'aquamarine')
    'aquamarineaquamarineaquama'

    Answer with looping:

    def mystery(n, word):
        result = ''
        while len(result) < n:
            result += word
        return result[:n]
    

    Answer without looping:

    def mystery(n, word):
        return (word*n)[:n]
    
  1. Find all the bugs in this function. It is supposed to take a constant and return a function which takes a number and scales (multiplies) the number by the constant. Then show how to write it with a lambda, and use it to make the function double (which takes a single number and returns double that number).
  2. def make-scaler(c)
        def scale(n)
            return c * n
      return('scale')
    

    Answer with errors removed (but indicated in comments)

    def make_scaler(c):   # 2 errors: '-' not allowed in names & missing ':'
        def scale(n):     # 1 error : missing ':'
            return c * n  # 0 errors
        return(scale)     # 2 errors: the indent was not consistent & no quotes
    

    Answer with lambda

    def make_scaler(c):
        return lambda n: c * n
    

    Creating add1

    >>> double = make_scaler(2)
    >>> double(3)
    6
    

Programs - Python-powered unit converter and Basic web programming

These programs will give you experience converting between units using Python (which will exercise your understanding of collections) and basic web fetching to highlight the power of using the web in your programs (and show you how easy it is to do in Python).

Related quizzes

Programming assignments

Readings

Quiz - Collections & Mutations

In this group, you will learn about Python's various techniques for maintaining collections of data and handling mutation. Again, there's a lot of reading, but by the end of this section, you will have significant Python fluency. You'll learn:

Readings

Sample Quiz

  1. What results would be printed by the following Python program?
  2. a = [1, 2]
    b = [1, 2]
    c = [6, 7, a]
    d = [6, 7, a]
    e = a + c
    f = [c, d]
    a.append(5)
    c[:1] = []
    
    print a
    print b
    print c
    print d
    print e
    print f
    

    Answers:

    [1, 2, 5]
    [1, 2]
    [7, [1, 2, 5]
    [6, 7, [1, 2, 5]]
    [1, 2, 6, 7, [1, 2, 5]]
    [[7, [1, 2, 5]], [6, 7, [1, 2, 5]]]
  1. Fill in the blanks with twenty words or less to describe what this function does: "The mystery function returns a dictionary that maps ______________ to ______________."
  2. def mystery(x):
        a = {}
        for y in x.split():
            z = y[0]
            if z not in a:
                a[z] = []
            a[z].append(y)
        return a
    

    Answer:

    "...a dictionary that maps each letter to a list of the words in x that begin with that letter."
  1. Suppose you type the following lines into a Python interpreter.
  2. b = 5, 6
    c, d = b
    a = b, c

    1. What are the type and value of a at this point?
    2. If you typed a += 2, what would happen to the value of a?
    3. If, instead, you typed a += b, what would happen to the value of a?
    4. If, instead, you typed a.append(b), what would happen to the value of a?

    Answers:

    1. a is the two-element tuple ((5, 6), 5).
    2. You would get a TypeError because tuples cannot be added to integers. The value of a would remain unchanged.
    3. The value of a would become ((5, 6), 5, 5, 6).
    4. You would get an AttributeError because tuples do not have an append method. The value of a would remain unchanged.
  1. The following two functions are intended to keep a record of a changing value.  Each time the value changes, change_to is called with the new value.  But there is a bug in one of these functions.  For each function, explain why it works correctly or describe the bug and show how to fix it.

    Function A (supposed to print the last value):

    oldvalue = None
    def change_to(newvalue):
        print 'previous value was', oldvalue
        oldvalue = newvalue

    Function B (supposed to print a list of all previous values):

    oldvalues = []
    def change_to(newvalue):
        print 'all previous values were', oldvalues
        oldvalues.append(newvalue)
    

    Answers:

    Function A contains the bug. The assignment oldvalue = newvalue makes oldvalue refer to a local variable, not the global variable that was set to None. Every time change_to is called, oldvalue will be a new unbound local variable and the print statement will cause a NameError. Insert the declaration global oldvalue at the beginning of the change_to function to fix the bug.

    Function B works correctly as given, because oldvalues.append(newvalue) is a method call, not an assignment. Since there are no assignments to oldvalues, oldvalues refers to the global variable and persists between calls to change_to as intended.

Program - Online Mad Libs®

This program will give you experience with CGI programming and Client/Server processing. Specifically:

Related quizzes

Programming assignments

Readings

Quiz - Input and Output

In programming assignment 2b, you learned how to pass data to and from a website. For this quiz, you will learn about how Python reads and writes information to files, otherwise known as Input and Output (I/O). Thankfully, there's not much reading in this section.

Readings

Sample Quiz

  1. The file input.txt contains a list of one or more floating-point numbers, with one on each line. An aspiring Python programmer wrote the following program to calculate the average of these numbers and write it to output.txt. However, the program has four bugs. Describe each of the bugs and write out a fixed version of program below.
  2. file = open('input.txt')
    total = 0.0
    count = 0
    while 1:
        line = file.readline()
        total += line
        count += 1
    file.close()
    file = open('output.txt')
    file.write(total/count)
    file.close()

    Answer:

    1. No termination of the while loop.
    2. The line of input is not converted to an number.
    3. Output file is opened for reading, not writing.
    4. A number instead of a string is passed to file.write().

    file = open('input.txt')
    total = 0.0
    count = 0
    while 1:
        line = file.readline()
        if not line:                     # Fix bug 1.
            break                        # Fix bug 1.
        total += float(line)             # Fix bug 2.
        count += 1
    file.close()
    file = open('output.txt', 'w')       # Fix bug 3.
    file.write('%f\n' % (total/count))   # Fix bug 4.
    file.close()

    Alternatively, bug 1 can also be fixed by replacing the lines

    while 1:
        line = file.readline()
    

    with the single line

    for line in file:
    

Program - Turtle Behavior

This program will give you experience with object-oriented programming in Python:

...by having you assign cool behavior to Turtles in a small artificial life program. Don't panic, we've done most of the heavy lifting for you!

Related quizzes

Programming assignments

Readings

Quiz - Object-Oriented Programming

For this quiz, you will learn about object-oriented programming in Python:

Readings

Sample Quiz

  1. Define a Counter class that supports the following interaction:
  2. >>> c1 = Counter()     ## default is to start counting from 0
    >>> c1.numCountersMade
    1                      ## Only c1 so far
    >>> c2 = Counter(100)
    >>> print(c1)
    Counter: 0
    >>> print(c2) 
    Counter: 100
    >>> c1.increment() 
    >>> print(c1)
    Counter: 1
    >>> print(c2) 
    Counter: 100
    >>> c1.numCountersMade
    2                      ## We've only made c1 and c2 so far
    >>> c2.clear()
    >>> print(c2)
    Counter: 0

    Answer:

    Here are thoughts as we approach the solution

    1. We somehow have to keep track of numCountersMade in a Class Variable so that as we make more Counters, it gets updated
    2. The argument to __init__ needs to be optional with a default value of 0
    3. We have to override __str__, since print knows what to do with a Counter
    4. We have to implement the methods increment() and clear()

    class Counter: 
      '''A simple counter'''
      numCountersMade = 0
    
      def __init__(self, count = 0): 
        '''Make a new counter'''
        self.count = count
        Counter.numCountersMade += 1
    
      def __str__(self):
        '''Override str'''
        return "Counter: " + str(self.count)
    
      def increment(self):
        '''Increment counter'''
        self.count += 1  
    
      def clear(self):
        '''Clear counter back to 0'''
        self.count = 0

  3. Now, we'd like to build a baseball Umpire class that can track the count of a hitter. Note, we wrote this code in a rush, there may be bugs in it! Your job is to find them and identify whether they are syntax, logical, or style bugs, then correct all the bugs.
  4. class Umpire
      '''A baseball umpire'''
    
      def __init__(self): 
        '''Make a new umpire'''
        self.balls = self.strikes = Counter()
    
      def __str__(self):
        '''Printed representation of an Umpire'''
        ### Want something like "3 balls, 2 strikes"
        print self.balls + " balls, " + self.strikes + " strikes"
    
      def strike(self):
        '''Register a strike, reset if 3 strikes'''
        self.strikes++
        if self.strikes.count == 3:
          clear(self)
          print "Strikeout"
    
      def ball(self)
        '''Register that we had a ball'''
        self.balls.increment()
        if self.balls.count == 4
          self.clear()
          print "Take your bases...a walk!"
    
      def clear(self):                   
        '''Clear the umpire counters'''
        self.balls.clear()
        self.strikes.count = 0

    Answer:

    Here are the problems we found (the corrected solution is below)

    1. Syntax: We need to import the Counter module if we're to use it in Umpire
    2. Syntax: We forgot a semicolon
    3. Logical: balls and strikes need to be different Counters, and not share the same Counter
    4. Syntax: To reference the Counter() initialiazation method (__init__) in the Counter class, we need to call Counter.Counter()
    5. Syntax: We need to access self.balls.count to get the number, not self.balls (an object), and we need to surround it with a str call
    6. Syntax: self.strikes is an object, and the ++ operator is from C/C++/Java, not Python.
    7. Syntax: clear will result in a NameError, since without the "self." it will look in the global namespace.
    8. Style: Even though explicitly changing the count will work given the current implementation of Counter, you are exploiting Python's "weakness" in its inability to limit access of its local instance variables from the outside. What if Counter.clear() were rewritten to effect other internal state in addition to zeroing out the clear variable? You'd be missing that, which would be a hard bug to track down. It's like going to a restaurant by coming through the back entrance in the kitchen...you sometimes miss the flyers they hand out at the door. Lesson: Always use official accessors ("setters" and "getters") when provided -- follow the Application Programming Interface (API), otherwise known as "specs". Granted, Counter doesn't provide a getter, and it probably should, but we can't change that in Umpire.

    import Counter                 ### bug 1
    
    class Umpire:                  ### bug 2
      '''A baseball umpire'''
    
      def __init__(self): 
        '''Make a new umpire'''
        self.balls = Counter.Counter()    ### bugs 3, 4
        self.strikes = Counter.Counter()  ### bugs 3, 4
     
      def __str__(self):
        '''Printed representation of an Umpire'''
        ### Want something like "3 balls, 2 strikes"
        return str(self.balls.count) + " balls, " + str(self.strikes.count) + " strikes"  ### bug 5
    
      def strike(self):
        '''Register a strike, reset if 3 strikes'''
        self.strikes.increment()   ### bug 6
        if self.strikes.count == 3:
          self.clear()             ### bug 7
          print "Strikeout"
    
      def ball(self):              ### bug 2
        '''Register that we had a ball'''
        self.balls.increment()
        if self.balls.count == 4:  ### bug 2
          self.clear()
          print "Take your bases...a walk!"
    
      def clear(self):                   
        '''Clear the umpire counters'''
        self.balls.clear()
        self.strikes.clear()       ### bug 8

Program - Turtle Arena

This program will give you experience with Tkinter and event-based programming in Python by asking you to modify the GUI we handed you in the Turtle Behavior project.

Related quizzes

Programming assignments

Readings

Quiz - Tkinter and Event-Based Programming

For this quiz, you will learn about Tkinter and event-based programming in Python:

Readings

Sample Quiz

  1. You be the packer. Sketch what the GUI will look like if we run the following simple code and then resize the window to be bigger (in both horizontal and vertical directions):
  2. from Tkinter import *
    Button(text="L").pack (side=LEFT,   expand=YES, fill=BOTH)
    Button(text="T").pack (side=TOP,    expand=YES, fill=BOTH)
    Button(text="R").pack (side=RIGHT,  expand=YES, fill=BOTH)
    Button(text="B").pack (side=BOTTOM, expand=YES, fill=BOTH)
    Button(text="LL").pack(side=LEFT,   expand=YES, fill=BOTH)
    Button(text="TT").pack(side=TOP,    expand=YES, fill=BOTH)
    mainloop()

    Answer:

  3. Now, we'd like a simple GUI that counts how many times a button (labeled "Click me") has been clicked and displays that number below the button saying: "i clicks", where i should be a number from 0 (at startup) to the number of clicks. Try to write it in the fewest number of lines. Here's a screenshot of what it should look like at startup:

  4. Answer:

    from Tkinter import *
    clicks=0
    
    def clicker():
        global clicks
        clicks += 1
        lab.config(text=clicks)
    
    Button(text="Click me", command=clicker).pack()
    lab = Label(text=clicks)
    lab.pack(side=LEFT)
    Label(text=" clicks").pack()
    
    mainloop()

  5. What is the strongest reason for using object-oriented programming with GUIs?
  6. Answer:

    There are many possible answers, but we believe the best is that one can customize an existing widget in a very interesting way (ala "Styles" in Microsoft Word) as a new class, and then use many instances in the final GUI. All the instances will have the same customization. If we didn't define a new class, then we'd have to apply the customization to each of the instances individually. These new classes can now be used by anyone, not just your program! Think software reuse!

  7. Now, we'd like to build a GUI that says "Demo GUI" and has a "Quit" button. Note, we wrote this code in a rush, there may be bugs in it! Your job is to find them and identify whether they are syntax, logical, or style bugs, then correct all the bugs...
    import Tkinter
    
    def quit():
        print "goodbye..."
        import sys; sys.exit()
    
    Label(text="Demo GUI")
    b = Button(text="Quit").pack(fill=yes)
    b.config(command=quit)
    root.mainloop()
  8. Answer:

    Here are the problems we found (the corrected solution is below)

    1. Style: It's better to use "from Tkinter import *" because otherwise you have to specify the explicit path for all of the widgets from the class. We provide both solutions below, you decide which you like better.
    2. Syntax: You need the explicit Tkinter.Label and Tkinter.Button paths (only a bug if they import Tkinter)
    3. Logical: The Label is never added to the GUI through a pack command
    4. Syntax: The method pack doesn't return a widget
    5. Syntax: fill doesn't take yes as an argument, it takes NONE, X, Y, or BOTH. expand (probably what they were thinking about) takes YES, though.
    6. Syntax: yes needs to be capitalized
    7. Syntax: YES needs to be Tkinter.YES (only a bug if they import Tkinter)
    8. Syntax: There is no root variable with a mainloop method. We can use b, however (or just mainloop if we use "from Tkinter import *").

    import Tkinter                        ### bug 1            
    
    def quit():
        print "goodbye..."
        import sys; sys.exit()
    
    Tkinter.Label(text="Demo GUI").pack() ### bug 2,3
    b = Tkinter.Button(text="Quit")       ### bug 2,4
    b.config(command=quit)
    b.pack(expand=Tkinter.YES)            ### bug 5,6,7
    ## Alternatively you collapse the above three lines into one with
    ## Tkinter.Button(text="Quit", command=quit).pack(expand=Tkinter.YES)
    b.mainloop()                          ### bug 8

    ...or...

    from Tkinter import *         ### bug 1            
    
    def quit():
        print "goodbye..."
        import sys; sys.exit()
    
    Label(text="Demo GUI").pack() ### bug 3
    b = Button(text="Quit")       ### bug 4
    b.config(command=quit)
    b.pack(expand=YES)            ### bug 5,6
    ## Alternatively you collapse the above three lines into one with
    ## Tkinter.Button(text="Quit", command=quit).pack(expand=YES)
    mainloop()                    ### bug 8