Why self?

Alex Martelli aleax at aleax.it
Tue Jul 9 17:08:19 EDT 2002


Charles Hixson wrote:
        ...
> Let's be explict:
> from __future__ import nested_scopes
> class A:
>     def ma(self):
>         print  "ma, ma"
>     def pa(self):
>         self.ma()
> class B(A):
>     def ma():
>         print "hello, world"
> tst = B()
> tst.pa()
> 
> Testing this produces the message, "hello, world"

This has been named the "Template Method" Design Pattern by Gamma et al,
in their wildly and deservedly successful book "Design Patterns", which
has given the Patterns movement such huge exposure.

Template Method is my equal-first candidate with Factories (which, however,
Gamma et al split into several patterns) as THE most powerful and
important design pattern of them all.

It doesn't show up well in this silly example, because A.pa has no
added value at all nor any reason for existing.  But it doesn't take
much to add "purpose" to Template Methods:

class A:
    def hi(self, *args): print "Hello, World!"
    def greet(self, n, *args):
        for i in range(n): self.hi(i, n, *args)

There you are -- a Serial Greeter.  Method greet encapsulates and
abstracts the functionality of repeating a greeting N times.  By
default each greeting is "Hello World!" on a separate line, but
it's not hard to start customizing this...:

class B(A):
    def __init__(self, separatelines=1):
        self.separatelines = separatelines
    def emit(self, lastline, message):
        print message,
        if lastline or self.separatelines: print
    def hi(self, i, n, *args):
        self.emit(i==n-1, "Hello, World!")

There -- now we've encapsulated the concept of making line separation
optional.  But, we aren't quite done yet...:

class C(B):
    message = "Hello, World!"
    def hi(self, i, n, *args):
        self.emit(i==n-1, self.message)

See what we have separated out now?  No?  What about...:

class D(C):
    message = "Hi there, Earthling..."

...starting to get it...?


The last step is not part of Template Method as published in the
Design Patterns book, but that's just because the languages they
had in mind (C++ and Smalltalk) aren't as smooth and flexible as
Python, and don't let subclasses override data, just methods.  In
Python, data-overriding is a very useful technique and a perfect
complement and completion of the Template Method DP.


> This seems to be the wrong message.  

You really need to get out more, you know.  Design Patterns has
been out for well over 5 years -- you can hardly call yourself an
OO programmer if you don't know it well!

> The version of pa that was called
> was the version defined in class A, so the routine called should have
> been the routine defined in class A.

Absolutely not!  That's the very reverse of OO programming.  Look
again:

>     def pa(self):
>         self.ma()

this very explicitly (and a good thing too, for all the self-haters!-)
says: call the version of ma defined ***ON self***.  It most clearly
does NOT say "call the version of ma defined ***in class A***!  Had
it wanted to say that, it would obviously be:

     def pa(self):
         A.ma(self)

and that would normally be a pretty silly thing (just like defining
a method as "final" in Java), but the language has not been and will
never be invented that _doesn't_ let you do silly things.


> Or, to me more accurate, for libraries to have maximal portability, I
> would want the message to have generated "ma, ma", and I had been

This behavior is perfectly portable among all platforms Python runs
on (and, as far as methods are concerned, also to all languages
that implement OOP).

> assuming that nested_scopes would result in that being the message that
> was produced.

nested_scopes has to do with nested scopes.  Classes inheriting from
each others are not scopes and are not nested, so it's peculiar that
you expected nested_scopes to affect them in any way.


Alex




More information about the Python-list mailing list