[Tutor] When to use a Class or just define functions?

Magnus Lyckå magnus@thinkware.se
Fri Jun 20 14:47:50 2003


I've had some wine with dinner, so if anything below
is confusing, I'll have to blame the Portugese...

At 06:41 2003-06-20 -0400, DAVID BABCOCK wrote:
>I have a module I wrote, but want to learn classes better, so was going to 
>make some changes.

I think you really need to write the program from
an object oriented perspective for classes to make
sense.

>I understand that Classes represent a powerful tool, like deriving from a 
>pre-existing Class or from a custom class. I understand that it groups 
>like functions together. And a few other things.

I'm not sure you are really on the right track.

If you have a number of related functions that you
just want to bundle, a module will be your best fit.

The purpose of a class is to bundle a data structure
which represents some logical entity with the operations
that work with this data structure.

For instance, we might imagine a logical entity such as
a text entry control in a GUI. That fits as a class. But
the point is not to bundle some functions.

The point is to capture an abstraction, and all that belongs
to it. This text entry control might have attributes--data
members, such as coordinate (x, y), size (w, h), background
and foreground colour, text content etc. It will also have
operations--methods such as getText, setText, enable, disable,
show, hide etc.

The common things about the functions is that they all act
on the same data, not that they are similar in any way.

>Like the module I mentioned first. It was just a few functions. Then added 
>this:
>
>class Quotes:
>      pass
>
>Very simple, but what 'extra' benefit does making a class and passing it, 
>really do?  I saw a difference when I used 'dir(quotes)'. But not sure it 
>really gave me anything.

Nothing is really added from making an empty class...

>My main point is when to make a 'new' class or just define a bunch of 
>functions? I could go further and include some of my common functions into 
>the new Quotes class. Not sure what benefit I will get from doing that though?

When you have a big system, with a lot of similar, but
maybe not identical types of data, it might be much
better to bundle it in classes. A GUI is a good example.

Or imagine that you have a number of geometrical shapes,
and you want to calculate areas for all of them.

import math

class Shape:
     def __init__(self, pos_x, pos_y):
         self.x = pos_x
         self.y = pos_y
     def area(self):
         # Not implemented in abstract base class
         pass

class Circle(Shape):
     def setRadius(self, r):
         self.radius = r
     def area(self):
         return math.pi * self.radius * self.radius

class Square(Shape):
     def setSide(self, s):
         self.side = s
     def area(self):
         return self.side * self.side

Let's imagine that you have ten different types of shapes.
Each shape knows how to calculate it's own area. If one
shape is changed or added, that means code changes in one
place.

You can do:

c1 = Circle(1, 4)
c1.setRadius(5)
c2 = Circle(6, 4)
c2.setRadius(3)
s1 = Square(10, 4)
s1.setSide(15)
s2 = Square(10, 14)
s2.setSide(5)

for shape in c1, c2, s1, s2:
     print shape.area()

In the code above, Circle is a class, an object that captures
the abstraction "circle". c1 is an instance of the Circle
class, an object that captures one actual circle. c1 and c2
are both circles. They share the same methods, but have
different data. (Same *kind* of data, but with different
values.)

The part of the code that does "c1.setRadius(5)" must "know"
that circles have a radius--that's unavoidable, but the for-
loop above only needs to know that each shape will return its
area as a number if you call its .area() method. It doesn't
care what kinds of shapes there are, or *how* areas are
calculated. This means that adding a new kind of shape (maybe
a star shape) doesn't cause more code changes than necessary.
This will make it easier to maintain and understand your code.

If you would want to do something like that without classes,
it could be made in different ways. Let's first imagine that
we have some kind of data structure, let's call it a struct
as in C, so that we have a circle object c1 with a radius
parameter. A C struct is basically a class without methods.
Then we will either have to have an area function that
understands all kinds of shapes. That would be used like this:

for shape in c1, c2, s1, s2:
     print area(shape)

The main disadvantage here is that the area function gets
more and more complex as the number of shapes increase.

The other option, is to have several simple area functions,
but then you move the complexity to the caller. It would
be something like:

for shape in c1, c2, s1, s2:
     if isCircle(shape):
         a = circle_area(shape)
     elif isSquare(shape):
         a = square_area(shape)
     ...
     print a

We could probably live with this as long as we only want
to calculate areas, but if we also want to be able to
display the shapes, move them, erase them, calculate
circumference, raise or lower them, etc etc, we will soon
see that it's a big advantage to be able to write code
that just handles a little part of the problem at a time.
It's about separation of concerns. A normal brain can't
manage to keep all balls in the air at once...

If we don't even have a shape structure, but need to store
data in lists etc, it would get even messier.

Classes aren't a silver bullet that solves all problems
quick and easy. For instance, if you want to calculate the
total area covered by all your shapes, you can't just sum
up all areas of individual shapes, since all areas where
different shapes overlap will be counted (at least) twice.

In thst case, the introvert perspective of each single class
will be inadequate. But then you might have another class,
maybe a surface class, that abstracts the concept of the
surface that is being covered. It all gets more complicated
though, because you will either need some mathematical
logic where one piece of code understands all different
shapes, so that it can calculate the size of the overlaps,
or else, you will have to perform som numerical approxiamtion
of what areas are covered, and then each class must be able
to tell you whether a certain point is within its surface
or not. (Then you will just do a Mone Carlo simulation: The
surface class will sample a number of coordinates of its
surface, and ask each shape whether that coordinate is inside
that shape or not.)

The way we have an abstract method Shape.area() which can be
called for all shapes, although different types of shapes
implement it in different ways, is called polymorphism. In
most object-oriented languages, inheritence is a requirement
to achieve polymorphism. I implemented it like that above.
All shapes (even if we only implemented Circle and Square)
will inherit from the base class Shape. If this had been
C or Java, this would have been a requirement for doing the
for-loop where we calculated all areas. The loop variable
"shape" would have been an instance of the abstract Shape
class.

In Python it's different. As long as all the items we loop
over has an area method, Python won't care if they have a
common base class or not. The lookup for an area method
is done when the program is executing the for loop, not
when it compiles the program.

Polymorphism is not the only advantage with classes though...

>I would guess that I could break the functions included in the new class 
>down further and they might be more useful to parts of the program that 
>didn't need the whole previous function. So wouldn't have to write a new 
>one. But could do that without the class, just might be a little more 
>confusing.
>Maybe just haven't gotten a complete grasp on it yet. Is it always 
>necessary to try and combine like fuctions in your programs into a class, 
>if not then when is it? What about modules you write, is it more important 
>to do it there?

When you write object-oriented programs, you don't structure
your programs functionally, based on the operations you
perform. You structure it in objects, based on what sets
of data you will perform operations on.

In other words, if you begin with a text (or spoken description)
that describes the system you are to build, you focus on the
important nouns in the description, rather than on the verbs.
Ok? As you might understand, the object oriented program will
often

A small Python script will often just work with one kind of
important object, and maybe only with one instance of the
object in each program run. Then there is little to gain in
using classes. The whole program would just be one single
class,


--
Magnus Lycka (It's really Lyckå), magnus@thinkware.se
Thinkware AB, Sweden, www.thinkware.se
I code Python ~ The Agile Programming Language