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)