[Tutor] Sorting Instance Attributes

Don Arnold Don Arnold" <darnold02@sprynet.com
Wed Dec 11 22:43:02 2002


> > Hi all--
> >
> > I wrote a program awhile ago to help me keep track of
> > attendance in my courses. Now, as an exercise, I've
> > decided to rewrite the program OOP-style. Among other
> > things, each student will be an object. The Student
> > class constructor begins with this code:
> >
> > class Student:
> >      def __init__(self, firstName, lastName):
> >           self.firstName=firstName
> >           self.lastName=lastName
> >
> > Adding a student into the course amounts to creating a
> > new instance of the Student class. Now, onto my
> > questions:
> >
> > 1. If I'd like to record most student information
> > within individual Student class instances, what would
> > be the recommended type (is that the right word?) to
> > use for storing the collection of Student objects?
> > List? Dictionary? I'll save the information via
> > cpickle or shelve.
>
> There is really no answer to this, depends on what you want to do with the
> "collection of Student objects." Think about how you want to manage this
> collection, what you want to do it. Is a list enough? Or probably it is
> better to roll out your own "collection" class?
>
> >
> > 2. [Here's the one that I'm stumped on...] How do I
> > sort objects via a single object attribute. In the
> > example above, suppose I'd like to create a class
> > list, sorted alphabetically via lastName?
>
> Here Python's magic methods are useful. Consider the following
>
> class Student(object):
>
>     #Methods snipped.
>
>     def __lt__(self, other):
>         return self.lastname < other.lastname
>
> I have added here one magic method - magic methods are signalled by being
> surrounded by double underscores - that tells Python that this class
> understands what is "less than". The method code is really simple: Since
you
> want alphabetical order we can just delegate to the order of builtin
string
> objects (There are probably some caveats with uppercase letters but let us
> forget that for a moment). For more details on magic methods consult the
> manuals.
>
> Now, if you have a list, l say, of student objects, you can just call
>
> l.sort()
>
> and it will automatically sort the list by calling the __lt__ method.
>
> Pretty cool stuff, I say.
>
> >
> > As always, thanks for your help! -- Al Colburn
> >
>
> With my best regards,
> G. Rodrigues
>

This got me to thinking (which can be dangerous at times): what if you don't
always want to sort by the same attribute? I played around a little, and
here's what I came up with:

def setStudentKey(key):
    """builds the comparison function on the fly"""
    c = 'def studentCompare(l,r):\n\treturn cmp(str(l.%s),str(r.%s))\n' \
        % (key, key)
    exec(c,__builtins__.__dict__)

class Student:
    def __init__(self,id,firstName,lastName,grade):
        self.id = id
        self.firstName = firstName
        self.lastName = lastName
        self.grade = grade

if __name__ == '__main__':
    theClass = []
    Mary = Student(1,'Mary','Smith', 93)
    Bob = Student(2,'Bob','Jones', 97)
    John = Student(3,'John','Albert', 70)

    theClass.append(Bob)
    theClass.append(John)
    theClass.append(Mary)

    for currKey in ('id','firstName','lastName','grade'):
        setStudentKey(currKey)
        theClass.sort(studentCompare)
        print 'class sorted by "%s":' % currKey
        for x in theClass:
            print '\t%s, %s, %s, %s' % (x.id,x.firstName,x.lastName,x.grade)
        print "\n"

When ran, this outputs:

class sorted by "id":
 1, Mary, Smith, 93
 2, Bob, Jones, 97
 3, John, Albert, 70


class sorted by "firstName":
 2, Bob, Jones, 97
 3, John, Albert, 70
 1, Mary, Smith, 93


class sorted by "lastName":
 3, John, Albert, 70
 2, Bob, Jones, 97
 1, Mary, Smith, 93


class sorted by "grade":
 3, John, Albert, 70
 1, Mary, Smith, 93
 2, Bob, Jones, 97

I realize that it's not perfect, but I was still kind of proud of myself. Of
course, I then realized that I was probably re-inventing the wheel, so I
whipped out my copy of 'The Python Cookbook'. Sure enough, recipe 2.6
('Sorting a List of Objects by an Attribute of the Objects') had quite
elegantly beaten me to the punch. Oh, well!

Don