[Tutor] Re: super classes

Christian Tismer tismer@appliedbiometrics.com
Sat, 27 Mar 1999 19:45:02 +0100


Art Siegel wrote:
...
> Now that I understand the terminology, a few more questions:
> 
> Java has a "super" function, I believe, which I assume allows one to
> directly access methods of the parent class from inside the child class.
> 
> Under what circumstances is it useful? Aren't, by definition, the parent's
> method already at the disposal of the child?

Usually yes. But sometimes your subclass or your instance defines
its own version of an attribute, and the parent's is hidden.
In Java, super allows you to access overridden methods.
I think, this is also used if one wants to use the inherited
function in the implementation of the new function. Something
for "do the old thing, but this also".

> What is the Python equivalent to the java super call?

There is no direct equivalent, but something alike.
If you have a derived class which redefines a method
of its superclass and you need to call that, you have
several choices:

If you know the name of the superclass, you can simply refer to
the class and use its method.

Here an example which might clarify this.

#from here
class classa:
	def say(self, text):
	  print "classa says '%s'" % text

class classb(classa):
	def say(self, text):
	  print "but classb says '%s'" % text

inst = classb()
#to here

Now, inst can say hello:

>>> inst.say("hello")
but classb says 'hello'
>>> 

What happens here is that the method "say" is looked up in
inst's __dict__, where it is not found, so it is looked
up in its class's __dict__ where it is found.
The unbound method "say" of classb is then bound to inst
(this means providing inst as parameter self) and called with
the additional arguments.
You can get the same result by calling th unbound method
by yourself:

>>> classb.say(inst, "hello")
but classb says 'hello'
>>>

The same way, we can call the superclass's method 

>>> classa.say(inst, "hello")
classa says 'hello'
>>> 

This is allowed if inst is an instance of that class or
any of its subclasses.

Now, what if you want to access a superclass method without
spelling its name?

>>> inst.__class__.__bases__[0].say(inst, "hello")
classa says 'hello'
>>> 

inst always has an attribute __class__ which is its class.
The class has an attribute __bases__ which is a tuple
with all the classes which appeared in the class statement.
In this case, there is just one entry, classa. In general,
you must expect that a class is a subclass of more than one
since Python has multiple inheritance. Then you need to know
the class' index in the __bases__ tuple to call the right
method.

A more common problem is to access a class variable instead
of the instance's. You know, every instance can redefine
any attribute, wether the class provides it or not.
I don't think it should be a recommended practice, but
a situation might appear where a user is allowed to
put arbitrary attributes into an instance, and you must
be sure to use always the class attribute

Assume the following situation:

>>> class circle:
...     defaultperimeter = 100
...     maximumperimeter = 100
...     def draw(self, x, y, perimeter):
...         if perimeter > circle.maximumperimeter:
...             perimeter = circle.defaultperimeter
...         print "drawing at (%d,%d) p=%d" % (x, y, perimeter)
... 	
>>> c=circle()
>>> c.draw(10,10,200)
drawing at (10,10) p=100
>>> c.defaultperimeter=1000 # ha, no chance!
>>> c.draw(10,10,200)
drawing at (10,10) p=100
>>> 

The instance cannot change the defaultperimeter.

>>> c.defaultperimeter
1000
>>> 

Observe, that I used the class name "circle" explicitly.
If I had referred to self.__class__, then a user could
overwrite the defaultperimeter again, like this:

>>> class circle:
...     defaultperimeter = 100
...     maximumperimeter = 100
...     def draw(self, x, y, perimeter):
...         if perimeter > self.__class__.maximumperimeter:
...             perimeter = self.__class__.defaultperimeter
...         print "drawing at (%d,%d) p=%d" % (x, y, perimeter)
... 
>>> class othercircle(circle):
... 	  defaultperimeter = 1000
... 	
>>> c=othercircle()
>>> c.draw(10, 20, 1000)
drawing at (10,20) p=1000
>>> c.defaultperimeter = 200
>>> c.draw(10, 20, 1000)
drawing at (10,20) p=1000
>>> 

As you can see, self.__class__ always referres to the actual
class of the object, no matter that draw was defined in
a different class.
Here we overwrote a class attribute by subclassing, where
the extra attribute of the instance was ignored.

> This is all coming up as I try to understand some java code and port it to
> Python.

What are you porting, btw?

ciao - chris

-- 
Christian Tismer             :^)   <mailto:tismer@appliedbiometrics.com>
Applied Biometrics GmbH      :     Have a break! Take a ride on Python's
Kaiserin-Augusta-Allee 101   :    *Starship* http://starship.python.net
10553 Berlin                 :     PGP key -> http://wwwkeys.pgp.net
PGP Fingerprint       E182 71C7 1A9D 66E9 9D15  D3CC D4D7 93E2 1FAE F6DF
     we're tired of banana software - shipped green, ripens at home