[Tutor] understanding classes

Gregor Lingl glingl@aon.at
Tue, 13 Aug 2002 00:25:11 +0200


This is a multi-part message in MIME format.
--------------010109070004060709070206
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Gus Tabares schrieb:

>Hello,
>
>	I'm reading Learning Python (O'Reilly) and I'm trying to grasp the concept
>of classes. In the book it has the example:
>
>		class FirstClass:
>			def setdata(self, value):
>				self.data = value
>			def display(self):
>				print self.data
>
>
>	Now I kind of understand what's going on here. It's defining a new class
>object called FirstClass. But what I don't understand really is the "self"
>part. The book says: "Functions inside a class are usually called method
>functions; they're normal defs, but the first argument automatically
>receives an implied instance object when called." I guess I'm just looking
>for a more simplied explanation of 'receives an implied instance object'. I
>sort of understand that if you set "x = FirstClass()" in this example 'self'
>inside FirstClass "becomes" x. Either that or I just don't know;)
>
>
>Thanks in advance,
>Gus
>
WARNING! WARNING! lenghty WARNING! WARNING!
                  but easy
WARNING!!     Tkinter needed      WARNING!!
          at least in your head

/*
 * I claim, that the following explanation
 * HELPS the students to develop a
 * correct - or at least useful and
 * usable mental model of the relationship
 * between classes, objects and the role
 * of the parameter self.
 *
 * I would ask you to show up any weak
 * points of this approach and especially
 * to check if there is some danger that the
 * students may develop a false and erroneous
 * or fundamentally incomplete view of the
 * topic?
 * Suggestions for improvement are equally
 * welcome!
 *
 * Many thanks in advance
 * Gregor
 */ 


I'll try to explain classes, objects and

      THE CASE OF THE UN-UNDERSTOOD SELF
     
in four steps - as concisely as I can.
I'll use a visible object in order to
'soften' the necessary abstractions.


I'll use the turtle - module.
The turtle is a commandable drawing-pen
with a local coordinate-system.

Thw turtle module contains
a) functions to command an anonymous turtle
b) a Class Pen to construct turtle-Objects,
   which posses exactly the functions from
   a) as methods, i.e. bound  functions
So we can decide, what to use.

--------------------------------------
Step 1: We use ordinary Functions
-------
We make a program, that defines a

function jump(distance)
which makes the turtle jump forward without
drawing. Such a function is not provided in
the module.
Then we let the turtle draw 30 pixels,
jump 30 pixels and go 30 pixels again.
The code of this program is as follows:

#######################################
# turtle01.py
""" make THE (anonymous) turtle jump
"""
from turtle import *

def jump(distance):
    up()
    forward(distance)
    down()

# now we can draw a broken line
forward(30)
jump(30)
forward(30)

Tkinter.mainloop()
############# SIMPLE ! ################
      
---------------------------------------
Step 2: We want to make several
concrete named turtles jump. So we have
to modify our previous program:
-------
#######################################
# turtle02.py
""" make SEVERAL NAMED turtles
(i. e. turtle-objects) jump
"""
from turtle import *

# jump now has to make som animal jump,
# so we have to deliver the information,
# which one:
def jump(turtle, distance):
    turtle.up()
    turtle.forward(distance)
    turtle.down()

# Make three turtles -
# they are objects, made from the class Pen

turtles = [leonardo, michelangelo, donatello] = [Pen(),Pen(),Pen()]

# let's turn two of them, so they march in
# different directions -
# so I've to call their methods, in this case method left(angle)

michelangelo.left(90)
donatello.left(180)

# now each of them can draw a broken line

for turtle in turtles:
    turtle.forward(30)
    jump(turtle,30)
    turtle.forward(30)

Tkinter.mainloop()
#######################################

We observe a lack of symmetry between
forward and jump - so we think, if we
can make this asymmetry vanish - lets
make our own turtles:

First inheritance comes to our mind!

We use our best friend for an intermediate
investigation:

 >>> from turtle import *
 >>> class MyPen(Pen):
    pass

 >>> leo = MyPen()
 >>> leo.forward(30)
 >>>

.... and it works! Leo behaves exactly like
leonardo in "Step 2", like a true Pen. But
he can't jump. So let's teach him (and his
brothers):

--------------------------------------
Step 3: We want to make our own turtles,
that can all turtles can + jump!
So let's modify turtle02.py
-------

######################################
# turtle03.py
from turtle import *

# jump now has to make some animal jump,
# so we have to deliver the information,
# which one. This time we do this by defining
# bound functions, i. e. methods:

# First we make our own Pen - class,
# inheriting from Pen

class MyPen(Pen):
    # to 'bind' jump we have to put it into
    # this class, by indenting it - AND NOTHING ELSE!!!
    # ... and keep in mind, why the parameter turtle is there!
    def jump(turtle, distance):
        turtle.up()              
        turtle.forward(distance)
        turtle.down()

# Make three turtles -
# they are objects, made from the class MyPen (!!!)

turtles = [leo, michel, tello] = [MyPen(),MyPen(),MyPen()]

# let's turn two of them, so they march in
# different directions -
# so I've to call their methods!
# In this case method left(angle)

michel.left(90)
tello.left(180)

# now each of them can draw a broken line
for turtle in turtles:
    turtle.forward(30)
    turtle.jump(30)     # now they know, how to do it!
    turtle.forward(30)

Tkinter.mainloop()
########################################

And we see, this works fine and produces the
same drawing as turtle02.py

--------------------------------------
Step 4: Cosmetics
-------
We know, we can choose parameter-names
as we like - and we are used to choose
them as meaningful as possible.

So we don't call the first parameter of
the jump - method zwrtlbrnft (which is
1. hard to remember for someone who is not
from Munich and
2. in no ways tied to the concepts of turtle
or class or object)
but think about the role of this parameter:
it references the animal, which has to jump.
And as the method is bound to it, for this
jumping animal the animal is simply (it)self!

So in the following there are only changes in
the method jump (only 4 lines!)

###### NEARLY NOTHING NEW ###############
# turtle04.py
from turtle import *

# jump now has to make some animal jump,
# so we have to deliver the information,
# which one. This time we do this by defining
# bound functions, i. e. methods:

# First we make our own Pen - class,
# inheriting from Pen

class MyPen(Pen):
    # to 'bind' jump we have to put it into
    # this class, by indenting it - AND NOTHING ELSE!!!
    # ... and keep in mind, why the parameter turtle is there!
    # rename turtle-->self
    def jump(self, distance):
        self.up()
        self.forward(distance)
        self.down()

# Make three turtles -
# they are objects, made from the class MyPen (!!!)

turtles = [leo, michel, tello] = [MyPen(),MyPen(),MyPen()]

# let's turn two of them, so they march in
# different directions -
# so I've to call their methods!
# In this case method left(angle)

michel.left(90)
tello.left(180)

# now each of them can draw a broken line
for turtle in turtles:
    turtle.forward(30)
    turtle.jump(30)     # now they know, how to do it!
    turtle.forward(30)

Tkinter.mainloop()
########################################

An this completes this demonstration of the
origin and the meaning of self.

=================== FINE ===========================

--------------010109070004060709070206
Content-Type: text/plain;
 name="turtle01.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="turtle01.py"

# Author: Gregor Lingl
# email: glingl@aon.at
# Datum: 12. 8. 2002
# turtle01.py
""" make THE (anonymous) turtle jump
"""

from turtle import *

def jump(distance):
    up()
    forward(distance)
    down()

# now we can draw a broken line
forward(30)
jump(30)
forward(30)

Tkinter.mainloop()


--------------010109070004060709070206
Content-Type: text/plain;
 name="turtle02.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="turtle02.py"

# Author: Gregor Lingl
# email: glingl@aon.at
# Datum: 12. 8. 2002
# turtle02.py
""" make SEVERAL NAMED turtles
(i. e. turtle-objects) jump
"""
from turtle import *

# jump now have to make som animal jump,
# so we have to deliver the information,
# which one:
def jump(turtle, distance):
    turtle.up()
    turtle.forward(distance)
    turtle.down()

# Make three turtles -
# they are objects, made from the class Pen
turtles = [leonardo, michelangelo, donatello] = [Pen(),Pen(),Pen()]
# let's turn two of them, so they march in
# different directions -
# so I've to call their methods!
# In this case method left(angle)
michelangelo.left(90)
donatello.left(180)

# now each of them can draw a broken line
for turtle in turtles:
    turtle.forward(30)
    jump(turtle,30)
    turtle.forward(30)

Tkinter.mainloop()    
    

--------------010109070004060709070206
Content-Type: text/plain;
 name="turtle03.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="turtle03.py"

# Author: Gregor Lingl
# email: glingl@aon.at
# Datum: 12. 8. 2002
# turtle03.py
""" make SEVERAL NAMED better educated turtles
(i. e. turtle-objects) that can jump
"""
from turtle import *

# jump now have to make some animal jump,
# so we have to deliver the information,
# which one. This time we do this by defining
# bound functions, i. e. methods:

# First we make our own Pen - class,
# inheriting from Pen
class MyPen(Pen):
    # to 'bind' jump we have to put it into
    # this class, by indenting it - AND NOTHING ELSE!!!
    # ... and keep in mind, why the parameter turtle is there!
    # this binding allows a different syntax for calling the function
    def jump(turtle, distance):
        turtle.up()
        turtle.forward(distance)
        turtle.down()

# Make three turtles -
# they are objects, made from the class MyPen (!!!)
turtles = [leo, michel, tello] = [MyPen(),MyPen(),MyPen()]
# let's turn two of them, so they march in
# different directions -
# so I've to call their methods!
# In this case method left(angle)
michel.left(90)
tello.left(180)

# now each of them can draw a broken line
for turtle in turtles:
    turtle.forward(30)
    turtle.jump(30)     # now they know, how to do it!
    turtle.forward(30)

Tkinter.mainloop()  
    

--------------010109070004060709070206
Content-Type: text/plain;
 name="turtle04.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="turtle04.py"

# Author: Gregor Lingl
# email: glingl@aon.at
# Datum: 12. 8. 2002
# turtle04.py
""" make SEVERAL NAMED better educated turtles
(i. e. turtle-objects) that can jump
"""
from turtle import *

# jump now have to make some animal jump,
# so we have to deliver the information,
# which one. This time we do this by defining
# bound functions, i. e. methods:

# First we make our own Pen - class,
# inheriting from Pen
class MyPen(Pen):
    # to 'bind' jump we have to put it into
    # this class, by indenting it - AND NOTHING ELSE!!!
    # ... and keep in mind, why the parameter turtle is there!
    # therefore change the NAME turtle to the NAME self
    def jump(self, distance):
        self.up()
        self.forward(distance)
        self.down()

# Make three turtles -
# they are objects, made from the class MyPen (!!!)
turtles = [leo, michel, tello] = [MyPen(),MyPen(),MyPen()]
# let's turn two of them, so they march in
# different directions -
# so I've to call their methods!
# In this case method left(angle)
michel.left(90)
tello.left(180)

# now each of them can draw a broken line
for turtle in turtles:
    turtle.forward(30)
    turtle.jump(30)     # now they know, how to do it!
    turtle.forward(30)

Tkinter.mainloop()    
    

--------------010109070004060709070206--