Modeling Motion Week 8 Computer Lab

Classes and Objects

To complete this worksheet you will create a Television class and populate it with attributes and methods then see if it works using a testing module. But first, to make things a bit easier, here is a review of classes and objects.

class Human:
    def __init__(self,first,last):
        self.firstname = first
        self.lastname  = last

This is a basic definition for a bizarre class of objects called Humans. Remember a class not the same thing as an object. It is like a plan or blueprint for a type of object (a "class" of objects). What distinguishes one class of objects from another (besides the name of the class) is the different attributes of objects that belong to that class. This class has one method attribute, the method __init__ which is called automatically everytime a new instance of class Human is created. The __init__ method has three parameters. The first one, self, is automatically supplied by Python and it refers to a brand new instance of a Human. The second and third parameters, first and last are the first and last names of the newly minted Human. In the body of the __init__ method are two assignments that give the new instance two new data attributes", firstname and lastname and assign to them the values of the first and last parameters respectively.

"Self" appears several times in the class definition which can be a hard concept to grasp. Self is the first parameter of every method's paramter list. The name "self" is arbitrary and has no special meaning to Python but is a bad convention to break. Methods belong to the class but not to individual objects because it would be wasteful for every object to carry around an identical copy of the same methods along with it. There may be many instances of that class in existence at any given time. When one of an instances methods gets called, the method needs to know which particular instances method got called. The method needs to know which instance called it because most methods will want to read or modify some of the data attributes of that instance. In the example below girl and guy are two objects of class Human but each has a different firstname attribute. One is "Bonney" and one is "Clyde". (And they would probably have gotten pretty upset of you got them confused.)

>>> girl = Human("Bonnie", "Parker")
>>> guy  = Human("Clyde", "Barrow")

The way the method knows which instance it is working for, is by automatically adding the instance itself as the first argument to each method call. Therefore a method's definition must always have a parameter representing which instance as its first parameter. In other words when Python converts the code, instance.method(arg1,arg2) internally to a call that looks like method(instance,arg1,arg2). By convention, this implicit first parameter is called self in a method's definition.

You might also want to consult How to Think Like a Computer Scientist, chapters 12, 13, and 14 for another explanation and more examples.

Writing the Television class

Now for the Television class. All of these numbered tasks below involve adding more and more code to the same class. Its not necessary to turn in individual responses to each one. Just send me your final version of a Television class definition with as many of the parts added and working as you can.

  1. Using the Human example above as a template, write a class called "Television" that has the following attributes:

    power

    whether the TV is "on" or "off"

    channel

    An integer from 1 to 70 representing the current channel

    Save the class definition in a file called timewasters.py. Give your class an __init__ method that takes no arguments (but will still have the implicit self in its definition) and assigns a default value of "off" for new Television objects power attribute and sets the channel to "2".

    Now run tvtest.py and you if everything is working you should already have 3 tests passing.

  2. The following version of the Human class has evolved the power of speech. I've added a new method called say which takes a string as an argument and prints it. In the method definition the required self argument is also included so the method knows who's doing the saying and can use their name.

    class Human:
        def __init__(self,first, last):
            self.firstname = first
            self.lastname  = last
            
        def say(self,words):
            print self.firstname +" says: "+words
    
    >>> baskin = Human("Burt","Baskin")
    >>> robbins = Human("Irv","Robbins")
    >>> baskin.say("I like ice cream.")
    Burt says: I like ice cream.
    

Using Human as an example, try adding a method to your Television class called whatsOn() that prints "Nothing good is on channel N" where N is replaced with the channel number the Television is currently on. Don't forget self in the parameter list. It should work like his:

>>> from timewasters import Television
>>> tv = Television()
>>> tv.whatsOn()
Nothing good is on channel 2

Once you get this working, tvtest should report 4 passing tests in addition to some failing ones from unimplemented parts of our Television class.

  1. Our TV isn't much good without a way to turn it on and back off. Write turnOn() and turnOff() methods which change the power attribute to "on" or "off" respectively. In addition, they should print "Power on" or "Power off" to let the user know what is happening. Remember when referring to the power attribute in the method definition, you will need to prefix it with self so you should refer it it as, self.power. The following statements should behave as shown:

    >>> tv.turnOn()
    Power on
    >>> tv.power
    on
    >>> tv.turnOff()
    Power off
    >>> tv.power
    off
    

    When this is working properly tvtest should report 8 passing tests.

  2. Now that we can turn our TV on and off we need to be able to change the channel. While it would be easier for users of this class to simply set the channel attribute directly (tv.channel=12), we would have no way of preventing the user from setting the channel to something that isn't in the range of valid channels. Lets say our TV can only show channels 1 through 70. Write a surfTo() method that takes a channel number as an argument and sets the channel to that number. In addition, it should tell the user what channel it switched to and ignore attempts to set the channel to something outside of the range of allowable channels. It should behave like this:

    >>> tv.surfTo(70)
    now on channel 70
    >>> tv.surfTo(71)
    channel 71 is out of range.
    >>> tv.channel #should still be on 70
    70
    >>> tv.surfTo(0)
    channel 0 is out of range.
    

    You should have 12 tests passing now in tvtest.

  3. Add a surfUp() method that adds one to the channel attribute and a surfDown() method that subtracts one. If the Television is at the maximum channel 70, and surfUp() is called, make it wrap around to channel 1. Conversely, if the channel is 1, then a call to surfDown() should wrap around to channel 70.

    >>> tv = Television()
    >>> tv.channel
    2
    >>> tv.surfUp()
    >>> tv.channel
    3
    >>> tv.surfDown()
    >>> tv.surfDown()
    >>> tv.channel
    1
    >>> tv.surfDown() # wrap around 
    >>> tv.channel
    70
    >>> tv.surfUp() # wrap back to 1
    >>> tv.channel
    1
    

    You should now have 19 tests passing.

  4. One thing we haven't considered is what to do while the TV is off. Lets assume nothing should work when the TV is off. This means if the TV is currently off, then each of the methods except for turnOn() and turnOff() should not work and should return some sort of message to the user to remind them the TV is off.

    Modify whatsOn() and all of the "surfing" methods so that when the power is off, they print out, "This TV isn't on." and return the boolean value False (return False). If the power is on, they should return True in addition to printing their normal message.

    Once this logic is in place, you should have all 30 tests passing. Congratulations if you've gotten this far.