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. fooこのようにして、すべての が同じ方法で比較され、すべての が同じ方法で比較されることがわかりますFunkyCmpFoo。そうしないと、最終的に変更され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 )

これを行う正当な理由の 1 つを考えることができます。それは、宿敵がコードをデバッグする必要がある場合です。実際、それを念頭に置いて行うための非常に楽しいことをいくつか思いつくことができます (すべての偶数より少ない数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

「アンチポリモルフォン」の響きが好きだったので、主にこの回答を投稿しました。適切な大人の監督なしで、子供たち、家でこれをしないでください.

[* 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 に答える