5

如何更改实例的 __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

4 回答 4

5

不要这样做

它会使您的代码出现错误且难以维护。之所以困难,是因为正确的做法是子类化foo

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

&c., &c. 这样,您就知道所有foos 都以相同的方式进行比较,并且所有FunkyCmpFoos 都以相同的方式进行比较。如果你不这样做,你最终将最终将修改后foo的内容与原件进行比较foo,而克苏鲁本人会从深处升起来惩罚你。

我不确定我是否应该这样说,但可以通过创建自己的实例方法

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如何比较小于所有偶数?)。除此之外,这是一场噩梦。

于 2010-08-03T15:24:31.427 回答
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
于 2010-08-03T15:26:12.247 回答
2

替代文字* 你可以使用反多态模式:

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

我主要发布了这个答案,因为我喜欢“anti-polymorphon”的声音。不要在家里这样做,没有适当成人监督的孩子。

[* 未经Jeff Atwood或权利持有人Steven C. McConnell许可使用编码恐怖标志,我敢肯定他是个大人物,不需要他们的标记像这样被玷污。]

于 2010-08-03T15:31:24.647 回答
0

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

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

在这种情况下,最好的方法通常是使比较函数成为对实例组进行操作的函数的参数,完全一样sorted

于 2010-08-03T17:33:01.063 回答