[Tutor] __cmp__() method examples?

dman dsh8290@rit.edu
Thu, 6 Dec 2001 17:52:16 -0500

On Wed, Dec 05, 2001 at 09:35:47PM -0600, Rob McGee wrote:
| {code}
| import random
| class Project:
|   def __init__(self, name):
|     self.name = name
|     self.value = random.randrange(1, 100)
|   def __str__(self):
|     return self.name
|   def __cmp__(self, other):
|     if self.name < other.name:  # compare name value (should be unique)
|       return -1
|     elif self.name > other.name:
|       return 1
|     else: return 0              # should mean it's the same instance
| # now generate class instances -- this is always done automatically
| from string import uppercase
| for letter in uppercase:
|   execStr = letter + ' = Project("' + letter + '")'
|   # example -- 'A = Project("A")', 'Z = Project("Z")
|   exec(execStr)
| {/code}
| (Sorry about the exec's, but I really don't know another way to do what
| I want. I want these instances in the global namespace, and I need to
| know exactly what each one is named. At least I think I do. Suggestions
| about other ways to approach this would be welcomed, too. :) Like with
| no exec's, or with the instances in something other than globals(), how
| would I be able to retrieve and alter their properties?)

Just put the objects in a container (ie list or dictionary) as others
have mentioned.  You do know exactly what it is named because it has a
'name' attribute.  When you think you need to exec to get extra local
variables, instead pretend exec doesn't exist (say, you're writing C
code) and use a container instead.

For example :

import string 
projects = []
for letter in string.uppercase:
   projects.append( Project( letter ) )

Now to show that you do know exactly what they are named :

for project in projects :
    print "this project is named " + project.name , project

This could be done with a dictionary if, for example, the class
already exists without a name, but you want to name it.

import string 
projects = {}
for letter in string.uppercase:
   projects[ letter ] = Project()

You still know exactly what each one is named :

for projectname in projects.keys() :
    print "this project is named " + projectname , projects[ projectname ]

| I'm getting errors which indicate that the class instances are being
| compared with other objects 

This is normal.  Objects can be compared to any other object at any
time.  The client code may know nothing about what your object is, but
it wants to know if it is equal to another one.

| what I'm hoping for is just a little guidance on how to properly
| implement a __cmp__() method.

IIRC __cmp__ isn't allowed to raise exceptions ...

| What I think I'll try -- the idea came to me while writing this :) -- is
| to put code in Project.__cmp__() like the ifInstance() above. If "other"
| isn't an instance of self.__class__, don't try to get other.name.

that's spelled  isinstance( instance_object , class_object ), and I
recommend against this.  What if someone else wants to use your
library, and they create a specialized project class that extends your
Project class via composition instead of inheritance?  In that case
the objects could never be equal.

Since you can't use the rich comparisions, here's what I would do :

def __cmp__( self , other ) :
    try :
        # the comparision is defined as the comparison of the strings
        return cmp( self.name , other.name )
    except :
        # must be a different kind of object,
        # make it less than us
        return -1

This method will succeed for all objects and give a total ordering,
though obviously the -1 in the except clause is arbitrary (just don't
make them equal ;-)).

| Oh, I should also mention that I got into an infinite loop with another
| class.__cmp__() method. That class was more complicated, and I had
| several if/elif loops to compare other attributes. The fourth and final
| such loop simply compared "self" and "other". I believe that was where
| the loop occurred, and it was in sorting a list of instances of that
| class (I don't think there were any non-instance members of the list.)
| By removing the self/other raw comparison I eliminated the infinite
| loop. But that experience gives me the idea that I'm missing something
| about how to do a Class.__cmp__().

Well, the __cmp__  method is supposed to decide whether or not the two
things are equal (and less than or greater than).  If you try and
compare the two objects directly using an operator, well, that
operator is defined by the __cmp__ method which tries to compare the
objects which is defined by __cmp__ (I'll stop here, but I think you
see what was happening).



"Don't use C;  In my opinion,  C is a library programming language
 not an app programming language."  - Owen Taylor (GTK+ developer)