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 "<s开发者_如何学JAVAtdin>", line 1, in <module>
#TypeError: cmp2() takes exactly 2 arguments (1 given)
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 foo
s compare in the same way, and all FunkyCmpFoo
s 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.
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
* 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.]
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.
精彩评论