对象与内置类型的比较
对象与内置类型的比较
Python文档明确指出x==y
调用x.__eq__(y)
。然而,在许多情况下,情况恰好相反。在何时或为什么会出现这种情况有什么文档记录,我怎样才能确定我的对象的__cmp__
或__eq__
方法是否会被调用。\n编辑:为了澄清,我知道__eq__
会优先调用__cmp__
,但是我不清楚为什么会优先调用y.__eq__(x)
而不是x.__eq__(y)
,当文档中说的是后者。\n
>>> class TestCmp(object): ... def __cmp__(self, other): ... print "__cmp__被调用" ... return 0 ... >>> class TestEq(object): ... def __eq__(self, other): ... print "__eq__被调用" ... return True ... >>> tc = TestCmp() >>> te = TestEq() >>> >>> 1 == tc __cmp__被调用 True >>> tc == 1 __cmp__被调用 True >>> >>> 1 == te __eq__被调用 True >>> te == 1 __eq__被调用 True >>> >>> class TestStrCmp(str): ... def __new__(cls, value): ... return str.__new__(cls, value) ... ... def __cmp__(self, other): ... print "__cmp__被调用" ... return 0 ... >>> class TestStrEq(str): ... def __new__(cls, value): ... return str.__new__(cls, value) ... ... def __eq__(self, other): ... print "__eq__被调用" ... return True ... >>> tsc = TestStrCmp("a") >>> tse = TestStrEq("a") >>> >>> "b" == tsc False >>> tsc == "b" False >>> >>> "b" == tse __eq__被调用 True >>> tse == "b" __eq__被调用 True
\n编辑:根据Mark Dickinson的答案和评论,似乎有以下规则:\n
- \n
- 富比较重写
__cmp__
__eq__
是__op__
的__rop__
(其他类似的规则适用于__lt__
,__ge__
等)- 如果左边的对象是内置类或新式类,并且右边是其子类,则在左边对象的
__op__
之前尝试右边对象的__rop__
\n
\n
\n
\n这解释了TestStrCmp
示例中的行为。 TestStrCmp
是str
的子类,但没有实现自己的__eq__
,因此在这两种情况下都优先调用str
的__eq__
(即tsc == \"b\"
调用b.__eq__(tsc)
作为__rop__
,因为规则1)。\n在TestStrEq
示例中,tse.__eq__
在两个实例中都被调用,因为TestStrEq
是str
的子类,所以它优先调用。\n在TestEq
示例中,TestEq
实现了__eq__
,而int
没有,所以两次都调用__eq__
(规则1)。\n但是,我仍然不明白第一个例子中的TestCmp
。 tc
不是int
的子类,所以 AFAICT应该调用1.__cmp__(tc)
,但事实并非如此。
在上述内容中,问题的原因是在比较两个对象时,Python首先会尝试调用对象自带的__eq__()
方法进行比较。如果对象没有定义__eq__()
方法,则会尝试调用__cmp__()
方法进行比较。因此,在比较两个对象A和B时,如果A定义了__eq__()
方法,则调用__eq__()
方法。如果A没有定义__eq__()
方法,则会调用__cmp__()
方法。
解决这个问题的方法是在对象中定义__eq__()
方法。通过在对象中定义__eq__()
方法,可以确保在比较对象时调用该方法,而不是默认的__cmp__()
方法。
同样的规则也适用于__ne__()
、__gt__()
、__ge__()
、__lt__()
和__le__()
等“rich comparison”方法。如果在对象中定义了这些方法,则会优先调用它们进行比较。
为了确保对象的比较行为符合预期,应当根据需要在对象中定义__eq__()
和其他相关的“rich comparison”方法。这样可以确保在比较对象时调用定义的方法,而不是默认的比较方法。
在Python的文档中,说明了如果没有定义rich comparison(富比较)方法,比较操作将调用__cmp__方法。在TestCmp类中,没有定义__eq__方法,因此调用了__cmp__方法。但是,str.__eq__方法是定义过的,所以可以推断TestStrCmp类继承了__eq__方法。尽管__eq__方法会覆盖__cmp__方法,但这并不是让人感到惊讶的行为。令人惊讶的是它将__cmp__方法调用到了右边的对象上而不是左边的对象上。
出现这个问题的原因是TestCmp类中没有定义__eq__方法,导致调用了继承自object类中的__eq__方法。而在object类中,__eq__方法会比较两个对象是否相等,如果不相等则调用__ne__方法进行比较。在比较的过程中,会调用__cmp__方法。由于TestCmp类中没有定义__cmp__方法,因此会报错。
解决这个问题的方法是在TestCmp类中定义__eq__方法,以覆盖继承自object类的__eq__方法。可以根据实际需求来定义__eq__方法的逻辑。这样就能避免调用__cmp__方法而引发错误。
以下是解决问题的代码示例:
class TestCmp(object): def __eq__(self, other): # Define the logic for comparing two TestCmp objects pass
通过定义__eq__方法,我们可以根据自己的需求来比较两个TestCmp对象的相等性,避免调用__cmp__方法带来的错误。
比较对象与内置类型的比较问题的出现原因是Python在比较操作时会调用特殊的方法,但当右操作数是左操作数类的子类的实例时,会首先调用右操作数的特殊方法。这种行为在Python的文档中有详细说明。解决方法是在比较操作时,要注意操作数的类关系和特殊方法的调用顺序。
在Python中,比较操作是通过特殊的方法来实现的。其中,特殊方法`__eq__`是相等比较的特殊方法,`__lt__`是小于比较的特殊方法,`__gt__`是大于比较的特殊方法,`__le__`是小于等于比较的特殊方法,`__ge__`是大于等于比较的特殊方法,`__ne__`是不等比较的特殊方法。这些方法可以通过重写来实现对象的比较操作。
然而,在比较操作中存在一些特殊情况。当左操作数是内置类型或新式类的实例,而右操作数是该类型或类的子类的实例,并且重写了基类的`__rop__`方法时,会首先调用右操作数的`__rop__`方法,然后才调用左操作数的`__op__`方法。这个特殊情况在Python的文档中有详细说明。
此外,文档中对比较方法的说明可能存在一些问题,需要进一步澄清。比如,文档中将`__lt__`作为`__gt__`的`__rop__`方法,但可能存在一些逻辑上的矛盾。关于`__cmp__`方法的规则也比较复杂,可能只在源代码中有详细说明。在Python 3中,移除了`__cmp__`方法,这是一个很好的改变。
总之,比较对象与内置类型的比较问题可能存在一些复杂的情况和不确定性,需要仔细理解和处理。希望在Python 3中能够更加清晰地解决这些问题。