Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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)
like image 953
DaniloNC Avatar asked Aug 03 '10 14:08

DaniloNC


2 Answers

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.

like image 97
Katriel Avatar answered Sep 19 '22 07:09

Katriel


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
like image 38
bstpierre Avatar answered Sep 23 '22 07:09

bstpierre