CS 151: Lab #10

Lab Exercise 10: Classes and Inheritance

Main course page

The purpose of this lab is to give you practice in creating a base class and many child classes that inherit methods and fields from the base class. In addition, we'll be modifying the L-system class to enable it to use rules that include multiple possible replacement strings and choose them stochastically (randomly).

We continuing to draw material from The Algorithmic Beauty of Plants. You can download the entire book from the algorithmic botany site


In lab today we'll be upgrading our L-systems and then building a parent class called Shape that will enable us to create quickly many other child classes that implement different kinds of shapes.

  1. On the Desktop, make a folder called lab10.
  2. Make a copy of your lsystem2.py file from Project 9 and rename it lsystem3.py.
  3. The only new capability we're going to add is the ability to use more than one replacement string in a rule. When making use of a rule with multiple replacement strings, the buildString function will randomly select one of the replacement strings.

    To make managing the rules easier, we're going to use dictionaries. The symbol to be replaced will be the dictionary key, and the dictionary value will the the list of one or more replacement strings.

    Start by editing your setRules function and assigning the empty dictionary to the rules field of self instead of the empty list. Because we're using the addRule method to actually add the rule itself, we don't have to change anything else.

    In the addRule function, you still need to copy the replacement strings (but not the symbol) from the rule into a new list. Then you can add the new symbol and replacement list pair to the dictionary.

    def addRule( self, rule ):
      # set a local variable to the empty list
      # for each replacement string in the rule (2nd element on)
        # append the element to the local list
      # set the dictionary entry for the key (rule[0]) to be the new list

    To add a new entry to a dictionary, you simply index the dictionary with the new key and make the assignment. For example, the following code snippet creates a new entry using the value in variable A as the key and makes the value in the variable B the entry.

    d = {}
    a = 'F'
    b = ['FF', 'FFF' ]
    d[a] = b

    The only other function we need to edit is the replace function, which needs to make use of the new dictionary form of the rules. The new form of the replace function is simpler than the old one, because we don't have to search for the right rule. Either the key exists or it doesn't, and we can ask the dictionary. Edit your replace function so it matches the following.

    def replace( self, istring ):
      # initialize the newstring to the empty string
      # for each symbol in the original string (istring)
        # if the symbol is in the rules dictionary
          # add to newstring a random choice from the dictionary entry self.rules[symbol]
        # else
          # add the symbol to newstring
      # return newstring

    Test your Lsystem by running its test function using one of the following files.

  4. We're now going to start building a system that makes use of our transformer class to build shapes that are not necessarily the output of L-systems. Just as we made functions in the second project to draw shapes, now we're going to make classes that create shape objects.

    Create a new file called shapes.py. Start by including the transformer class using from transformer import *. Make sure the module name matches the name of the file in which you define Transformer.

  5. Begin a parent class called Shape. Then define an __init__() function that takes self and three arguments with default values: distance, angle, and color. Color should be a 3-element tuple like (0, 0, 0).

    The init function should create the object fields distance, angle, color, and string and set them to the arguments of the init function, except for string, which should be set to the empty string.

  6. Create accessor functions setColor(self, r, g, b), setDistance(self, value), setAngle(self, angle), and setString(self, value).
  7. Create a function draw() that executes the following algorithm.
    def draw(self, xpos, ypos, scale=1.0, orientation=0):
      # create a Transformer object
      # have the Transformer object place the turtle at (xpos, ypos)
      # have the Transformer object orient the turtle to orientation
      # have the Transformer object set the turtle color
      # have the Transformer object draw the string
      #    Note: use the distance, angle, and string fields of self
      #    Note: multiply the distance by the scale parameter of the function
  8. In the same file, but below the Shape class definition, begin a new class Square that inherits Shape.

    class Square(Shape):

  9. Have the __init__() function of the Square class take two optional arguments: distance and color. The __init__() function should do the following.
    def __init__(self, distance=100, color=(0, 0, 0) ):
      # call the parent's __init__ function with distance and color
      # use the setString method of self to set the string to 'F-F-F-F-'
      # use the setAngle method of self to set the angle to 90
  10. In the same file, below the Square class, create a new class called Triangle that is the same as the Square class, but sets the string to 'F-F-F-' and sets the angle to 120.
  11. Once you have completed your Square and Triangle classes, download and run the testshapes.py program to see if your classes work properly.

When you are done with the lab exercises, go ahead and get started on the assignment.