Re: [Edu-sig] Explaining Classes and Objects

It may be that I have the joy of teaching 10 year olds, and you have to deal with more experienced folk. raise "that was stupid!" #translate that into your language has enormous 'teacher-as-hero' value around here. I know all the arguments as to why string exceptions should be removed from python. I even agree with them. But raise 'you are an idiot!' in some circles has its own sort of charm .... Laura ps -- i am not entirely sold on oo programming. clearly it is the correct way to model certain problems, but a more functional approach seems better suited to other kinds. I think teaching TDD is more important than OO these days. Am I only reflecting my own loves and predjudice? In a message of Mon, 13 Jun 2005 09:13:55 PDT, "Kirby Urner" writes:
In a message of Sun, 12 Jun 2005 22:08:50 PDT, "Kirby Urner" writes:
OO really is a different world. I think it still makes sense to teach the subject historically, even though we *can* start with objects at the same time. In other words, have students relive some of the traum a of moving from the procedural paradigm to the object oriented one -- not to traumatize, but to give the flavor of what "paradigm" even mean s (including that switching between them may be difficult).
This may make sense for people who already know procedureal programming , but I don't think that it is a good idea for the rest of the world. I think that you need to learn about exception handling _early_ ... say right after you learn how to write a loop, and unit testing, and test driven design from the moment you get out of the 'how to play with the interpreter' stage.
The average beginner isn't going to know any programming, so why not star t with class/object right from the top?
Yet the procedural model requires less setup I think, less background. There's no "class hierarchy" to discuss.
This is where the parallel experience with math, in algebra, will help, a s in Python we have top-level functions, and procedural programming is main ly just organizing the flow in terms of a main procedure branching to and returning from numerous subprocedures e.g.:
def main(args): vars = sub1(args) vars = sub2(vars) vars = sub3(vars) return vars
Procedural code starts as simply as:
def f(x): return x*x
My approach is to start with a lot of a rule-based numeric sequences (the above generates square numbers): triangular numbers, Fibonacci numbers, other polyhedral numbers. This means writing a bunch of free-standing functions. But then suddenly you need one to call the other, i.e. when doing tetrahedral numbers, you need to stack consecutive triangles:
def tri(n): return n*(n+1)//2
def tetra(n): sum = 0 for i in range(n): sum += tri(i) # <-- function calling function return sum
There was an earlier paradigm shift for the first users of procedural languages, such as FORTRAN. Formerly, it was OK to use a very convoluted flow based on GOTO -- lots of jumping around, giving us "spaghetti code." The Dykstra came along and preached the gospel of "structured programming " (more like the above). A lot of programmers fought the idea of a "right" way to do flow.
Having listened to more than a couple CS teachers describe their curricul a, I'm seeing the procedural-first, OO-next approach is fairly widespread. The good thing about Python though is the OO model is deeply engrained, and j ust to begin explaining dot-notation is to enter the class/object discussion.
What I come to is it's easier to think of objects as something you use, as
primitives in the language, within procedural flows. Then take what you' ve learned about writing functions to write methods and start rolling your o wn classes. At that point, the full power of the OO model will start to daw n.
What you say about getting into exceptions early is food for thought. I think you're probably right. Error handling is where the procedural flow gets especially ugly, what with all those case-based validations (switch statements trying to pin down all the ways a piece of data might be wrong ).
However, once again I think students may gain a deeper appreciation for exception handling once they've been exposed to the old way of doing it. But that doesn't mean weeks and months of coding in an "obsolete" style. One could go through the history in a short lecture, with projected examples.
There isn't a lot of use in teaching people the procedural necessity of doing a huge amount of data validation, and how to deal with the 'canno t happen' of malformed data. It makes for really ugly code -- where it i s close to impossible to find out what the code is supposed to do when nothing is wrong, and everything is working properly, because over 80% of it is to detect problems you hope you will never see ...
just my 2 cents, Laura
Yes, you've got a point. Validation still consumes lines of code, and we still prompt the user to try again, but the code isn't so gnarly.
Kirby

Laura Creighton:
I think teaching TDD is more important than OO these days. Am I only reflecting my own loves and predjudice?
I agree the TDD (test driven development) is getting a lot of focus these days. I don't regard TDD and OO as mutually exclusive though -- possible to learn both -- at the same time even. Kirby

ps -- i am not entirely sold on oo programming. clearly it is the correct way to model certain problems, but a more functional approach seems better suited to other kinds.
I think teaching TDD is more important than OO these days. Am I only reflecting my own loves and predjudice?
Hi Laura, If so, it's a prejudice that's shared by a few other people. *grin* Oleg Kiselyov has a paper called "Subclassing errors, OOP, and practically checkable rules to prevent them": http://okmij.org/ftp/Computation/Subtyping/ where he brings up the idea that subclassing can be tricky, and that OOP in general can be more complex just because one has to hold a class hierarchy in one's head to know exactly what's going on. One of the references in his paper is also interesting: http://research.microsoft.com/Users/luca/Papers/BadPropertiesOfOO.html The attributes of a good "engineering language" in Luca's terms might not necessarily be an ideal "teaching language". But I think it's useful to read some of the criticism from the procedural side of things, to better understand the strengths and weaknesses of OOP. Best of wishes to you!

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)
participants (3)
-
Danny Yoo
-
Kirby Urner
-
Laura Creighton