[Edu-sig] Explaining Classes and Objects

Kirby Urner urnerk at qwest.net
Tue Jun 14 22:49:46 CEST 2005


Here's an approach to learning dot-notation:  take common household
appliances that have user controls, and implement them as Python classes.

For example, a TV typically has a volume control that goes louder and
quieter, but doesn't allow numeric inputs, and doesn't exceed upper and
lower bounds.  

The channel control typically goes "around the dial" meaning you can use the
"higher" or "lower" direction (here symbolized with "+" and "-") to go
around and around -- *or* you may enter the channel number directly.  

Adjusting volume and/or channel only makes sense if the TV is on.

I've tried to capture the above logic in the class definition below.  I'm
using a variety of techniques, including some exception handling and type
checking around input validation.  Various conditionals, but no loops.

All the state variables are private.  The user is supposed to use the
__repr__ method to get a state report.  This design is not very friendly to
external classes -- but why should it be?  It's simulating a TV, which is
pretty much a closed box from the user's point of view.

Typical session:
 
 >>> from hhobjects import TV
 >>> mytv1 = TV('Bedroom')
 >>> mytv2 = TV('Living Room')  # two TVs makes the object/class distinction
 >>> mytv1.power()
 >>> mytv1.volume('+')
 >>> mytv2.channel(3)           # ...each TV has its own state
 TV Living Room is turned off
 >>> mytv2.power()
 >>> mytv2.channel(3)
 >>> mytv2
 TV Living Room:  power: on channel: 3  volume: 0
 >>> mytv1
 TV Bedroom:  power: on channel: 0  volume: 1
 >>> mytv1.channel('-')
 >>> mytv1
 TV Bedroom:  power: on channel: 99  volume: 1

If doing this in a classroom, we might have students interact with the TV
class for awhile, then start looking at source code.  In a more advanced
class, we might specify the API, and ask students for their own class
definitions.  Intermediate level:  the code below could be improved with
some refactoring, plus we'd like a brightness control -- go for it.

Next lesson:  "how to pickle your television for later"

Kirby

====

class TV:
    """
    Simulate TV with power, channel, volume controls
    """

    channels = 100 # range is 0-99
    
    def __init__(self, label):
        """
        Initialize a TV with power off
        """
        self.label = label
        self._power = 'off'
        self._channel = 0
        self._volume = 0

    def power(self):
        """
        Power is an on/off toggle
        """
        if self._power == 'on':
            self._power = 'off'
        else:
            self._power = 'on'

    def _checkpower(self):
        """
        Channel or volume changes are inactive if power is off
        """
        try:
            assert self._power=='on'
        except:
            print "TV %s is turned off" % self.label
            return False
        return True
                
    def volume(self, val):
        """
        Volume may go one notch higher or lower within range,
        will not exceed limits
        """
        try:
            assert type(val)==type("") and val in '+-'
        except:
            print "+ or -"
            return
        
        if self._checkpower():
            if self._volume > 0 and val == '-':
                self._volume -= 1
            if self._volume < 10 and val == '+':
                self._volume += 1

    def channel(self, val):
        """
        Channel may go higher or lower, or supports direct entry
        of channel number.  Going higher over the top, or below 0,
        takes you "around the dial"
        """
        if self._checkpower():
            if type(val)==type(1):
                try:
                    assert TV.channels >= val >= 0
                except:
                    print "Channel out of range."
                self._channel = val                
            elif val == '-':
                self._channel = (self._channel - 1) % TV.channels
            elif val == '+':
                self._channel = (self._channel + 1) % TV.channels
            else:
                print "+, -, or channel number"
            
    def __repr__(self):
        """
        Used for checking state.  TV does not want users directly
        accessing its internal state variables
        """
        if self._power == 'on':            
            return "TV %s:  power: %s channel: %s  volume: %s" \
                   % (self.label, self._power, self._channel, self._volume)
        if self._power == 'off':
            return "TV %s:  power: %s" % (self.label, self._power)
        



More information about the Edu-sig mailing list