Unification of Methods and Functions

James Moughan moughanj at tcd.ie
Sun May 23 09:18:54 EDT 2004


David MacQuigg <dmq at gain.com> wrote in message news:<pndva0hi800r5c132j22jv42djd36not8m at 4ax.com>...
> On 22 May 2004 11:50:18 -0700, moughanj at tcd.ie (James Moughan) wrote:
> 
> >David MacQuigg <dmq at gain.com> wrote in message news:<9hgpa0d2iq5bb2ko4sngs5i6v4og90vqej at 4ax.com>...
> >> On 19 May 2004 23:26:38 -0700, moughanj at tcd.ie (James Moughan) wrote:
> >> >David MacQuigg wrote:
>  
> >> I like your example, but not as a *substitute* for Animals_2.  It's
> >> just too complex for non-CIS students at this point in the course.  I
> >> think I understand your concerns about Animals_2.  I think the best
> >> way to deal with those concerns is not by complicating the example, or
> >> by taking out everything that could lead to problems, but rather by
> >> pointing out the problems, and showing subsequent examples that fix
> >> those problems, even at the expense of more complexity.
> >> 
> >> If you have a simple all-in-one alternative to Animals_2, show me.
> >> 
> >
> >Again, clearly I have not communicated myself well - I mean a
> >*completely* different example to the entire Animals approach.  The
> >difficulty with writing a good Animals-type example comes from the
> >things which it is trying to do, which aren't especially well
> >expressed by a class-heirarchy.
> 
> What I am trying to do in Animals_2 is provide the simplest, most
> learnable example of Python OOP, which has at least one of every basic
> structure the students will encounter in a real program.  So I include
> bound, unbound, and static methods, but not metaclasses or
> descriptors.
> 
> Creating a good example like this is turning out to be more of a
> challenge than I expected.  I'm fairly happy with Animals_2, but I
> appreciate the "maintainability" problems you have pointed out, and
> have added footnotes accordingly.  The examples in Learning Python are
> too spread out in little pieces, and of course, no attention to the
> issues you are concerned about.  If you know of another place I can
> look for a good all-in-one teaching example, I am interested.
> 

Just for the sake of example, this is one of the programs which I
always write while learning a language, written in a way I'd use to
demonstrate OO; all it does is read in a wordlist, then print out
synonyms for user-inputed words.  Handy for crosswords. ;-)

First in a data/tool centric style, which could be one of the earlier
examples:

def addItem(dict, key, elem):
    if dict.has_key(key):
        dict[key].append(elem)
    else:
        dict[key] = [elem]

def sortWord(word):
    word = map(str.lower, word)
    word.sort()
    return ''.join(word)

def main():
    multi_dict = {}

    for line in open("wordlist.txt").readlines():
        line = line.strip()
        addItem(multi_dict, sortWord(line), line)
    while True:
        word = sortWord(raw_input('Enter a word to find synonyms'))
        if multi_dict.has_key(word):
            print multi_dict[word]
            #lis = multi_dict[word]
            #lis.append('blah') <- error!!
        else:
            print 'No synonyms in the worlist.'

main()

Not a bad solution, but inflexible.  Also a program which repeats the
pattern in the commented-out lines will cause problems.  So, we really
want to encapsulate the data in a class;

class Multi_dict:
    def __init__(self):
        self._dict = {}
    def add(self, key, elem):
        if self._dict.has_key(key):
            self._dict[key].append(elem)
        else:
            self._dict[key] = [elem]
    def get(self, key):
        if self._dict.has_key(key):
            return list(self._dict[key])
        return []

def sortWord(word):
    word = map(str.lower, word)
    word.sort()
    return ''.join(word)

def main():
    multi = Multi_dict()

    for line in open("wordlist.txt").readlines():
        line = line.strip()
        multi.add(sortWord(line), line)
    while True:
        word = sortWord(raw_input('Enter a word to find synonyms: '))
        syns = multi.get(word)
        if len(syns) > 0:
            print syns
        else:
            print 'Sorry, no synonyms'

main()

This is a generalised solution to making a multi-entry dictionary. 
However, we still need to call sortWord, which is a shame; someone
might try using two different functions to hash the elements, and it
makes our code sloppier.
However, if we put it into a class sortWord doesn't really need a self
attribute; so we make it a static method.  We also extend the
Multi_dict class to avoid replicating the code.

class Multi_dict:
    def __init__(self):
        self._dict = {}

    def add(self, key, elem):
        if self._dict.has_key(key):
            self._dict[key].append(elem)
        else:
            self._dict[key] = [elem]

    def get(self, key):
        if self._dict.has_key(key):
            return list(self._dict[key])
        return []

class Thesaurus(Multi_dict):
    def __init__(self):
        Multi_dict.__init__(self)

    def sortWord(word):
        word = map(str.lower, word)
        word.sort()
        return ''.join(word)
    sortWord = staticmethod(sortWord)

    def add(self, elem):
        Multi_dict.add(self, self.sortWord(elem), elem)

    def get(self, elem):
        return Multi_dict.get(self, self.sortWord(elem))

def main():
    theo = Thesaurus()

    for line in open("wordlist.txt").readlines():
        line = line.strip()
        theo.add(line)
    while True:
        word = raw_input('Enter a word to find synonyms: ')
        syns = theo.get(word)
        if len(syns) > 0:
            print syns
        else:
            print 'Sorry, no synonyms'

main()

Exercises for students: (i.e. stuff I was too lazy to do)
1) Modify the original program to terminate on an empty input line
2) Extend Multi_dict to create a class which stores only one example
of each item. (Class heirarchy.)

I think this shows everything you want in simple way, with the
exception of class-level data - you can show that by moving _dict to
class level, demonstrating a system which pools all of the words
entered into any Thesaurus, and point out the advantages/disadvantes
of this - there may be examples where this would be just what you
want.

You could pick an example from an area which your students are
familiar with to make whings more concrete.

> >Why not just take one of your earlier program examples, show the
> >students what it looks like when object-oriented, then extend the
> >class(es)?
> >
> >As I've mentioned, I don't like my example *at all* for your teaching
> >purposes, and I'm not suggesting you use it.  It was simply to
> >illustrate that there is another method of doing things, and what the
> >original problems were.  (A good solution to the Animals 'problem'
> >would be simple, it just wouldn't demonstrate any of the things you
> >want to show.)
> 
> Then it wouldn't be a good solution.  What I want to show is at least
> one of every structure needed in a real program.
> 
> I've made your example one of the exercises.  It solves a particular
> problem, and it is worth showing.
> 
> >> We also need to make sure we include *all* children of Reptile in the
> >> new numReptiles function.
> >> 
> >> class Reptile(Animal):
> >>    -numReptiles = 0
> >>    def numReptiles():
> >>       return ( Reptile._numReptiles +
> >>          Snake.numSnakes() + Lizard.numLizards() )
> >> 
> >> Oops.  We forgot Geckos !!
> >> 
> >> To avoid these errors in programming, we may need a function that
> >> automatically searches for subclasses, and calls their num functions.
> >> I'm not sure how to do this without making it excessively complex.
> >> Suggestions will be appreciated.
> >> 
> >> -- Dave
> >
> >Hmm, I think perhaps I like your original example better than this.
> >:-\   It's a bit like treating a cut throat with a tourniquet.
> >
> >Searching the subclasses will AFAIK still require some kind of
> >metaprogramming, which is not too suitable for a 4-hour beginner
> >course on OO.
> 
> Most likely, we will not cover the solutions to the maintainability
> problems, but we will mention them so students are aware.  Electronic
> design engineers are good at structured, hierarchical, robust design,
> so I won't have to spend a lot of time on encapsulation, modularity,
> maintainability, etc.  They are well aware of the need for protection
> circuits on all inputs.  Pointing out the problems in a simplified
> program example should be sufficient.
> 
> -- Dave



More information about the Python-list mailing list