如何更改实例的__cmp__函数(不在类中)?

时间:2010-08-03 14:58:07

标签: python metaprogramming cmp

如何更改实例的__cmp__函数(不在类中)?

例如:

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)

4 个答案:

答案 0 :(得分:5)

不要这样做

它会使你的代码变得越来越难以维护。困难的原因是因为正确的方式是将foo子类化:

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

&amp; c。,&amp; c。这样,您就知道所有foo都以相同的方式进行比较,并且所有FunkyCmpFoo以相同的方式进行比较。如果你不这样做,你最终会最终将修改后的foo与原始foo进行比较,而Cthulhu本人将从深处崛起来惩罚你。

我不确定是否应该这样说,但it is possible,创建自己的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 )

我可以想到一个很好的理由去做,那就是你的大敌必须调试代码。事实上,我可以想到一些非常有趣的事情要考虑到这一点(您希望sys.maxint如何比较少于所有偶数?)。除此之外,这是一场噩梦。

答案 1 :(得分:2)

编辑:如果你在生产代码中执行此操作,那么我应该说你是一个坏人。你的所有头发和牙齿都会掉下来,你会被诅咒在你的来世永远地踩着堆叠。

添加额外的间接位,这样就不会混合绑定/未绑定的方法:

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

答案 2 :(得分:2)

alt text *您可以使用反多态模式:

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

这给出了明智的结果,如:

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

我大多发布这个答案,因为我喜欢“反多态”的声音。如果没有适当的成人监督,请不要在家中这样做。

[*未经Jeff Atwood许可使用的编码恐怖标识或权利持有人Steven C. McConnell,我确信这些标识会膨胀,并且不需要像这样玷污他们的标记。]

答案 3 :(得分:0)

虽然通常更改类的不同实例的比较函数是不好的做法,但有时您可能希望对要执行常规操作的一组实例使用不同的比较函数。例如,您希望根据不同的标准对它们进行排序。

标准示例为sorted(list_of_foos, cmp = foocmp)。尽管目前首选使用key参数,但事实上Python 3.x不支持cmp参数(您可能希望使用cmp_to_key)。

在这种情况下,最好的方法通常是使比较函数成为在实例组上运行的函数的参数,与sorted完全相同。