开发者

Why/When in Python does `x==y` call `y.__eq__(x)`?

开发者 https://www.devze.com 2022-12-20 05:41 出处:网络
The Python docs clearly state that x==y calls x.__eq__(y).However it seems that under many circumstances, the opposite is true.W开发者_JAVA百科here is it documented when or why this happens, and how c

The Python docs clearly state that x==y calls x.__eq__(y). However it seems that under many circumstances, the opposite is true. W开发者_JAVA百科here is it documented when or why this happens, and how can I work out for sure whether my object's __cmp__ or __eq__ methods are going to get called.

Edit: Just to clarify, I know that __eq__ is called in preferecne to __cmp__, but I'm not clear why y.__eq__(x) is called in preference to x.__eq__(y), when the latter is what the docs state will happen.

>>> class TestCmp(object):
...     def __cmp__(self, other):
...         print "__cmp__ got called"
...         return 0
... 
>>> class TestEq(object):
...     def __eq__(self, other):
...         print "__eq__ got called"
...         return True
... 
>>> tc = TestCmp()
>>> te = TestEq()
>>> 
>>> 1 == tc
__cmp__ got called
True
>>> tc == 1
__cmp__ got called
True
>>> 
>>> 1 == te
__eq__ got called
True
>>> te == 1
__eq__ got called
True
>>> 
>>> class TestStrCmp(str):
...     def __new__(cls, value):
...         return str.__new__(cls, value)
...     
...     def __cmp__(self, other):
...         print "__cmp__ got called"
...         return 0
... 
>>> class TestStrEq(str):
...     def __new__(cls, value):
...         return str.__new__(cls, value)
...     
...     def __eq__(self, other):
...         print "__eq__ got called"
...         return True
... 
>>> tsc = TestStrCmp("a")
>>> tse = TestStrEq("a")
>>> 
>>> "b" == tsc
False
>>> tsc == "b"
False
>>> 
>>> "b" == tse
__eq__ got called
True
>>> tse == "b"
__eq__ got called
True

Edit: From Mark Dickinson's answer and comment it would appear that:

  1. Rich comparison overrides __cmp__
  2. __eq__ is it's own __rop__ to it's __op__ (and similar for __lt__, __ge__, etc)
  3. If the left object is a builtin or new-style class, and the right is a subclass of it, the right object's __rop__ is tried before the left object's __op__

This explains the behaviour in theTestStrCmp examples. TestStrCmp is a subclass of str but doesn't implement its own __eq__ so the __eq__ of str takes precedence in both cases (ie tsc == "b" calls b.__eq__(tsc) as an __rop__ because of rule 1).

In the TestStrEq examples, tse.__eq__ is called in both instances because TestStrEq is a subclass of str and so it is called in preference.

In the TestEq examples, TestEq implements __eq__ and int doesn't so __eq__ gets called both times (rule 1).

But I still don't understand the very first example with TestCmp. tc is not a subclass on int so AFAICT 1.__cmp__(tc) should be called, but isn't.


You're missing a key exception to the usual behaviour: when the right-hand operand is an instance of a subclass of the class of the left-hand operand, the special method for the right-hand operand is called first.

See the documentation at:

http://docs.python.org/reference/datamodel.html#coercion-rules

and in particular, the following two paragraphs:

For objects x and y, first x.__op__(y) is tried. If this is not implemented or returns NotImplemented, y.__rop__(x) is tried. If this is also not implemented or returns NotImplemented, a TypeError exception is raised. But see the following exception:

Exception to the previous item: if the left operand is an instance of a built-in type or a new-style class, and the right operand is an instance of a proper subclass of that type or class and overrides the base’s __rop__() method, the right operand’s __rop__() method is tried before the left operand’s __op__() method.


Actually, in the docs, it states:

[__cmp__ is c]alled by comparison operations if rich comparison (see above) is not defined.

__eq__ is a rich comparison method and, in the case of TestCmp, is not defined, hence the calling of __cmp__


As I know, __eq__() is a so-called “rich comparison” method, and is called for comparison operators in preference to __cmp__() below. __cmp__() is called if "rich comparison" is not defined.

So in A == B:
If __eq__() is defined in A it will be called
Else __cmp__() will be called

__eq__() defined in 'str' so your __cmp__() function was not called.

The same rule is for __ne__(), __gt__(), __ge__(), __lt__() and __le__() "rich comparison" methods.


Is this not documented in the Language Reference? Just from a quick look there, it looks like __cmp__ is ignored when __eq__, __lt__, etc are defined. I'm understanding that to include the case where __eq__ is defined on a parent class. str.__eq__ is already defined so __cmp__ on its subclasses will be ignored. object.__eq__ etc are not defined so __cmp__ on its subclasses will be honored.

In response to the clarified question:

I know that __eq__ is called in preferecne to __cmp__, but I'm not clear why y.__eq__(x) is called in preference to x.__eq__(y), when the latter is what the docs state will happen.

Docs say x.__eq__(y) will be called first, but it has the option to return NotImplemented in which case y.__eq__(x) is called. I'm not sure why you're confident something different is going on here.

Which case are you specifically puzzled about? I'm understanding you just to be puzzled about the "b" == tsc and tsc == "b" cases, correct? In either case, str.__eq__(onething, otherthing) is being called. Since you don't override the __eq__ method in TestStrCmp, eventually you're just relying on the base string method and it's saying the objects aren't equal.

Without knowing the implementation details of str.__eq__, I don't know whether ("b").__eq__(tsc) will return NotImplemented and give tsc a chance to handle the equality test. But even if it did, the way you have TestStrCmp defined, you're still going to get a false result.

So it's not clear what you're seeing here that's unexpected.

Perhaps what's happening is that Python is preferring __eq__ to __cmp__ if it's defined on either of the objects being compared, whereas you were expecting __cmp__ on the leftmost object to have priority over __eq__ on the righthand object. Is that it?

0

精彩评论

暂无评论...
验证码 换一张
取 消