views:

85

answers:

4

How can i change the __cmp__ function of an instance (not in class)?

Ex:

class foo:
    def __init__(self, num):
        self.num = num

def cmp(self, other):
    return self.num - other.num

# Change __cmp__ function in class works
foo.__cmp__ = cmp
a = foo(1)
b = foo(1)

# returns True
a == b



# Change __cmp__ function in instance that way doesnt work
def cmp2(self, other):
    return -1

a.__cmp__ = cmp2
b.__cmp__ = cmp2

# Raise error 
a == b
#Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#TypeError: cmp2() takes exactly 2 arguments (1 given)
+5  A: 

DO NOT DO THIS

It will make your code buggy and hard to maintain. The reason it is difficult is because the right way to do it is to subclass foo:

class FunkyCmpFoo( foo ):
    def __cmp__( self, other ):
        return -1

&c., &c. This way, you know that all foos compare in the same way, and all FunkyCmpFoos compare in the same way. If you don't, you will eventually end up comparing a modified foo with an original foo, and Cthulhu himself will rise from the depths to punish you.

I'm not sure whether I should say this, but it is possible, by creating your own instance methods:

funcType = type( foo.__cmp__ )
# Alternatively:
import new
cmp2 = new.instancemethod( func, a, foo )

a.__cmp__ = funcType( cmp2, a, foo )
b.__cmp__ = funcType( cmp2, b, foo )

I can think of one good reason to do this, and that is if your archenemy has to debug the code. In fact, I can think of some quite fun things to do with that in mind (how would you like sys.maxint to compare less than all even numbers?). Apart from that, it's a nightmare.

katrielalex
Thanks!I won't make this in a "real" code, it is only for fun.
DaniloNC
+2  A: 

Edit: This is the part where I'm supposed to say you're a bad person if you do this in production code. All your hair and teeth will fall out, and you'll be cursed to walk the stack forever during your afterlife.

Add an extra bit of indirection so you're not mixing up bound/unbound methods:

class foo(object):
    def __init__(self, num):
        self.num = num
        self.comparer = self._cmp

    def __cmp__(self, other):
        return self.comparer(self, other)

    @staticmethod
    def _cmp(this, that):
        print 'in foo._cmp'
        return id(this) == id(that)

    def setcmp(self, f):
        self.comparer = f

def cmp2(self, other):
    print 'in cmp2'
    return -1

a = foo(1)
b = foo(1)

print a == b

a.setcmp(cmp2)
b.setcmp(cmp2)

print a == b
bstpierre
You should at least say not to do that, or someone actually *might* =p
katrielalex
+2  A: 

alt text* You can use the anti-polymorphon pattern:

class foo(object):
    def __init__(self, num, i_am_special=None):
        self.num = num
        self.special = i_am_special

    def __cmp__(self, other):
        if self.special is not None:
            return -1
        else:
            return cmp(self.num, other.num)

    def __hash__(self):
        if self.special is not None:
            # TODO: figure out a non-insane value
            return 0

Which gives sensible results like:

>>> a = foo(1)
>>> b = foo(2, 'hi mom')
>>> a > b
False
>>> b > a
False
>>> a == b
False
>>> b == b
False

I mostly posted this answer because I liked how "anti-polymorphon" sounded. Don't do this at home, kids without proper adult supervision.

[* coding horror logo used without permission of Jeff Atwood or the rights holder Steven C. McConnell who I'm sure are swell guys and don't need their mark sullied like this.]

msw
A: 

While it is bad practice to change the comparison function for different instances of a class in general, sometimes you may want to use a different comparison function for a group of instances for which you want to do a common operation. An example is when you want to sort them according to different criteria.

The standard example would be sorted(list_of_foos, cmp = foocmp). Notwithstanding that it is currently preferred to use the key parameter and in fact Python 3.x doesn't support the cmp parameter anyway (you would want to use cmp_to_key).

In this case the best way is usually to make the comparison function a parameter of the function operating on the group of instances, exactly as sorted does.

Muhammad Alkarouri