[Tutor] Objects (was Re: Truckers Log....Me Again)

Paul Sidorsky paulsid@shaw.ca
Mon, 21 Jan 2002 00:35:42 -0700


Before I start, I don't think I ever introduced myself to the list even
though I've been here for a while and posted a few times.  So I'll do it
now, though I hate to make a very long post even longer:

I'm 25 and a 3rd year Computer Science student at the local University,
but I've been programming since I was 7 (first in BASIC, then C, then
C++) so I'm mostly there to get the piece of paper (though I am
nevertheless learning a lot).  The data structures course introduced me
to Python last year, and I've been hooked ever since.  I wish I had
discovered it many years ago, before I started C++.  It would have
bridged the gap nicely.  Anyhow I like to help out people with
programming when I can, so that's why I'm here.  Plus the odd time I
might even need to ask a question myself!

Erik Price wrote:

> Admittedly, I am new so I don't know anything.  But it would seem to me
> that this "while" loop would never end, since the condition "1"
> evaluates to "true" infinitely.  I would assume that this is for the
> sake of example, and not to demonstrate an actual program's pseudocode.
> But on this list I have seen more than one example use this convention.
> Is Python different from other languages in this respect?

It's pretty natural to mix Python and pseudocode in examples because
Python has such a clean syntax.  As for the specific example, Python has
no "do...while" construct (that is, a loop that evaluates the
conditional at the end rather than the beginning) so the usual
workaround is this:

while 1:
    # do stuff
    if loop_condition:
         break

Danny's pseudo-Python didn't show a break but it would have to be
included somewhere of course.  Similarly, my example, while valid
Python, needs to have something inserted where the comment is to make
loop_condition true or it will also run on forever.

> Another question I have is about objects.  I appreciate the "dumb 
> question" thread from earlier this weekend, since I do not completely 
> grasp the notion of objects myself.  I have done a brief tutorial in 
> PHP's classes from a book, but haven't seen a situation where it would 
> be helpful to me so far.  (Though this could easily be due to my 
> inexperience in programming.)

Objects have a lot of power and they make life a lot easier.  The big
problem is that the benefits tend to be more long-term in nature so they
can't easily be seen.  It doesn't help that a lot of OOP tutorials try
to explain them by using fancy words like "encapsulation" which just
seem to make people more confused.

I'll try to illustrate some of their advantages through a very simple
example.  (All code is untested.)

Without objects:

seconds = 0
minutes = 0
hours = 0

def setclock(h=0, m=0, s=0):
    global hours, minutes, seconds
    hours = h
    minutes = m
    seconds = s   

def tickclock():
    global hours, minutes, seconds
    seconds += 1
    if seconds == 60:
        seconds = 0
        minutes += 1
        if minutes == 60:
            minutes = 0
            hours += 1
            if hours == 24:
                 hours = 0

def printclock():
    print "%02i:%02i:%02i" % (hours, minutes, seconds)

if __name__ == "__main__":
    setclock(12, 0, 0)
    while 1:
        printclock()
        tick()

With objects:

class Clock:
    def __init__(self, hours=0, minutes=0, seconds=0):
        self.hours = hours
        self.minutes = minutes
        self.seconds = seconds

    def tick(self):
        self.seconds += 1
        if self.seconds == 60:
            self.seconds = 0
            self.minutes += 1
            if self.minutes == 60:
                self.minutes = 0
                self.hours += 1
                if self.hours == 24:
                     self.hours = 0

    def __repr__(self):
        return "%02i:%02i:%02i" % (self.hours, self.minutes, 
                                   self.seconds)

if __name__ == "__main__":
    myclock = Clock(12, 0, 0)
    while 1:
        print myclock
        myclock.tick()

On the surface, the object version looks simpler to write and about the
same to use.  So all this junk about putting self in front of everything
and defining special behaviours (__init__ and __repr__) doesn't seem
worth the bother.  The two versions are identical in functionality, so
what is the point of using an object here when it doesn't seem to be
needed?

The first big payoff is in reuse.  If, down the road, you want to put a
clock in another program, with the object version you can do this:

import cheapclock

anotherclock = cheapclock.Clock(12, 30)

#do some stuff

anotherclock.tick()
print anotherclock

You can do something similar with the non-object version, but it's
uglier:

import cheapclock
cheapclock.hours = 12
cheapclock.minutes = 30

...

cheapclock.tick()
cheapclock.printclock()

Imagine doing that for many more variables and functions.  <shudder> 
Also, what if you need more than one clock?  I'm not even sure you can
do it with the non-object version.  With the object version it's trivial
to make even a thousand clocks:

clocklist = []
for i in range(1000):
    clocklist.append(cheapclock.Clock())

Another huge win with the object version comes in maintenance.  If you
come back to the first version many months later, it takes a while to
get reacquainted.  You have to find out where hours, minutes, etc. are
declared and used.  You may have to relearn the function names.  With
the second version you can instantly see that minutes, hours, seconds
belong to the Clock, and that the clock can tick itself and print
itself.  (No need to find the name of the print function because you can
just print the clock object itself!)

Lastly, extension is much easier.  Lets say later on you want a clock
that has milliseconds.  You can't change your cheapclock module above
because you have other programs using it the way it is, so these
extensions will have to be done in a different module.  This is quite
simple with the object version:

import cheapclock

class MilliClock(cheapclock.Clock):
    def __init__(self, hours=0, minutes=0, seconds=0, ms=0):
        cheapclock.Clock.__init__(hours, minutes, seconds)
        self.milliseconds = ms

    def tick(self):
        self.milliseconds += 1
        if self.milliseconds = 1000:
            self.milliseconds = 0
            cheapclock.Clock.tick(self)

    def __repr__(self):
        return cheapclock.Clock.__repr__(self) + ("%03i" %
self.milliseconds)

With the non-object version, what do you do?  Probably you copy the old
cheapclock module and hack it to include milliseconds.  That'll mean
changing a lot of those "global" lists, among other things.  Even once
you do that, what if there's a bug in the original code that you've also
copied?  Now you have to change it in two places.  Ditto if you want to
make an enhancement to the general cheapclock and use it in your
millisecond clock as well.  With the object version, though, you can
make the change in the original cheapclock module and not even touch the
millisecond version, yet still get its benefits.

It's tough to convey the power of objects in a single message and with
such a small example, but I hope this provides some insight into WHY one
might use them.

> One of the reasons Python is recommended so heavily is that it is "true
> object oriented".  And in response to 14-yr old Jimmy's request for
> programming assistance, another python-tutor-lister recommended becoming
> familiar with the use of objects.  Is the use of objects somehow more
> tightly integrated into Python than in other languages such as PHP and
> Perl?  I had assumed that I could learn Python's syntax and hopefully
> get started with some small-scale development as a learning exercise,
> and worry about objects later.  But is it a better idea to try to tackle
> this topic as early on as possible?

I would suggest trying to learn them as soon as you have the
fundamentals of the language down (loops, functions, etc.).  The problem
if you don't is that you start to learn ways to compensate for their
absence, and often these habits are VERY bad and make your life much
more difficult than it needs to be.  I mentioned a few of the problems
these habits might cause in my example above, but that's just the tip of
the iceberg.

The big problem with objects in Python is that it's easy to largely
ignore them.  While everything in Python is an object, you don't have to
know this to use the language.  This is in contrast to a language like
Java where you must consciously define at least one new class for every
program.  In Python you can go a long time and do a lot of neat stuff
without ever defining your own classes or even using multiple modules of
your own.  So to learn to use objects in Python does take some
discipline.  The benefits, though, are well worth it.

-- 
======================================================================
Paul Sidorsky                                          Calgary, Canada
paulsid@shaw.ca                        http://members.shaw.ca/paulsid/