[Tutor] OO scripting in python...

Daniel Yoo dyoo@hkn.eecs.berkeley.edu
Sat, 26 May 2001 01:46:27 -0700 (PDT)


On Wed, 23 May 2001, GADGIL PRASAD     /INFRA/INFOTECH wrote:

> I am coming from moderate C, basic C++ knowledge, and some perl exp.

Hello; I hope you're having a good time learning Python.  I'll try to take
a shot at giving a small...er...long... overview of OOP.


> For OO scripting, the basic concepts I learned during c++ seems rather
> unconnected when I read py-tutorial chapter 9 (classes).  It seems
> individual languages like perl, python implement various basic
> concepts of OO in such different ways, with such varying grammer,
> especially with perl, I was totally confused with 'perltoot' (perl-

This is surprising; from what I remember, Perl's object orientedness is
actually based on Python's model.  Granted, the way that Perl implements
objects might look weird at first, but the idea of object oriented
programming, that information can be associated with a bunch of actions,
is the same.


> tom christian's O. O. tutorial') It seemd c++ is rather straight
> forward than that.

No comment about C++.  *grin*

For an introduction to OOP, I really like Damian Conway's "Objected
Oriented Perl" book.  The first chapter dedicates itself to explaining
what OOP is all about, so you might want to go to your local bookstore,
browse through chapter one, and then things might make more sense.


Before understanding OOP, it will help by seeing a few things that might
be already familiar to you --- from there, the idea of object oriented
programming might make more sense.

First an analogy to a computery thing: If we're on a Windows system, we
can take a look at one of our file directories, and press the right mouse
button on a file.  What appears on our screen is a list of actions that we
can perform.  "What sort of things can files do?"  --- this is a question
that's phrased in object-oriented terms.


At the heart of OOP is the idea that we have a thing, some data, that can
perform actions for us.  This is something that's totally natural to us:
As another example, if we have a car, we know that we can do certain
things with it.

    1.  Drive it around the block.
    2.  Wash it.
    3.  Crash it.

And we also know that a car has an identity, some data... like a license
plate number, or the color of the paint job.  So we can view a car, in
programming terms, as a structure or record.  Let's represent a car as a
dictionary,

###
def makeCar(plate_number, paint_job):
    return { 'plate_number' : plate_number,
             'paint_job' :  paint_job }
###

that we associate with certain actions:

###
def driveCar(car):
    print "Just drivin' my ", car['plate_number'], "around the block"

def washCar(car):
    print "The", car['paint_job'], "is starting to shimmer and shine."

def crashCar(car):
    print "Oh no!  We just dented our paint job!"
    car['paint_job'] = '*dented*' + car['paint_job'] + '*dented*'
###


Let's try it out:

###
>>> mycar = makeCar('1800ONERING', 'gold')
>>> mycar
{'paint_job': 'silver', 'plate_number': '1800ONERING'}
>>> driveCar(mycar)
Just drivin' my  1800ONERING around the block
>>> washCar(mycar)
The gold is starting to shimmer and shine.
>>> # muhahah
... 
>>> crashCar(mycar)
Oh no!  We just dented our paint job!
>>> washCar(mycar)
The *dented*gold*dented* is starting to shimmer and shine.
###


Believe it or not, that's practically most of what OOP is all about: when
we're writing programs that deal with data, we sometimes want to emphasize
that certain functions ("methods") should be really tied together with
that data.  In fact, much of the machinery in Python is there just to
prevent us from doing silly things like:

###
>>> washCar('foo!')
The
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/usr/tmp/python-1762VZD", line 12, in washCar
TypeError: sequence index must be integer
###

just because it shouldn't make any sense to call washCar() on anything but
a Car made by our makeCar() function.


With this in mind, it might be instructive to look at how a Car would look
like, if we started using all the machinery Python gives us to make things
nicer looking.  Time to start using the "class" keyword.

###
class Car:
    def __init__(self, plate_number, paint_job):
        self.plate_number = plate_number
        self.paint_job = paint_job

    def drive(self):
        print "Just drivin' my ", self.plate_number, "around the block"

    def wash(self):
        print "The", self.paint_job, "is starting to shimmer and shine."

    def crash(self):
        print "Oh no!  We just dented our paint job!"
        self.paint_job = '*dented*' + self.paint_job + '*dented*'
###


The ideas here are the same; we're defining how to create a car with the
'__init__' definition (taking the place of makeCar()), and we tell Python
what sort of things a Car can do --- drive, wash, and crash! --- and the
code to use it looks similar:

###
>>> mycar = Car('1800RULETHEM', 'Aluminum')
>>> mycar.drive()
Just drivin' my  1800RULETHEM around the block
>>> mycar.wash()
The Aluminum is starting to shimmer and shine.
>>> mycar.crash()
Oh no!  We just dented our paint job!
>>> mycar.wash()
The *dented*Aluminum*dented* is starting to shimmer and shine.
###


Let's compare the two implementations of Cars: we'll see that this "class"
based approach emphasizes the activity of the car itself.  That is,
instead of saying "crashCar(car)", as if car were a passive thing being
acted upon, we're now saying "car.crash()".  We are commanding a poor,
hapless car, "car, crash for me!"  Passive voice and active voice?  
Hmmm...

Anyway, In C++, in Perl, in Python, all these mechanisms are there to make
our life easier.  I know the car analogy is stretching things a little bit
though: for a more realistic example, let's go back to files.  If we look
at what Python files are really like:

###
>>> myfile = open('carreports.txt', 'w')
>>> myfile
<open file 'carreports.txt', mode 'w' at 0x812cdb0>
###

it might come as a surprise to see that files in Python are object
oriented:

###
>>> dir(myfile)
['close', 'closed', 'fileno', 'flush', 'isatty', 'mode', 'name', 'read',
'readinto', 'readline', 'readlines', 'seek', 'softspace', 'tell',
'truncate', 'write', 'writelines']
###

There's our list of actions that files know how to do: they can be read(),
write()ed, seek()ed, and close()ed, among other things.  (Sorry about the
mangling of English there...)  So a file is some thing, some object, that
has a bunch of actions that we associate it with.


How about strings?  What can they do?

###
>>> mystring = 'hello'
>>> dir(mystring)
['capitalize', 'center', 'count', 'encode', 'endswith', 'expandtabs',
'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace',
'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace',
'rfind', 'rindex', 'rjust', 'rstrip', 'split', 'splitlines', 'startswith',
'strip', 'swapcase', 'title', 'translate', 'upper']
###

Most likely you've already been working with object orientation quite a
bit: we just haven't been _calling_ it OOP since it sounds intimidating at
first.

There's a little more about OOP that we haven't touched yet (inheritence),
nor have we even talked about the "Why?" part:  Why do we do this OOP
stuff in the first place?  What are the advantages?  As a short answer:
it's usually easy for a programmer to handle objects since they're in
nice, neat packages, compared to functions that seem disconnected from the
data they work with.  But this message is getting too long already, so I'd
better stop for now.

I hope that this clears some things up; if you have more questions, feel
free to ask us.  Good luck to you.