Почему / Когда в Python `x == y` вызывает` y .__ eq __ (x) `? - PullRequest
37 голосов
/ 17 февраля 2010

Документы Python четко заявляют, что x==y вызывает x.__eq__(y). Однако, похоже, что при многих обстоятельствах происходит обратное. Где это задокументировано, когда или почему это происходит, и как я могу точно определить, будут ли вызваны методы __cmp__ или __eq__ моего объекта.

Редактировать: Просто чтобы уточнить, я знаю, что __eq__ вызывается предпочтительнее __cmp__, но я не понимаю, почему y.__eq__(x) вызывается предпочтительнее x.__eq__(y), когда последний является тем, что документы состояние случится.

>>> 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

Редактировать: Из ответа и комментария Марка Дикинсона может показаться, что:

  1. Богатое сравнение переопределений __cmp__
  2. __eq__ это собственный __rop__ к __op__ (и аналогичный для __lt__, __ge__ и т. Д.)
  3. Если левый объект является встроенным классом или классом нового стиля, а правый является его подклассом, правый объект __rop__ пробуется перед __op__

Это объясняет поведение в TestStrCmp примерах. TestStrCmp является подклассом str, но не реализует свой собственный __eq__, поэтому __eq__ из str имеет преимущество в обоих случаях (т.е. tsc == "b" вызывает b.__eq__(tsc) как __rop__ из-за правило 1).

В примерах TestStrEq, tse.__eq__ вызывается в обоих случаях, потому что TestStrEq является подклассом str и поэтому вызывается в предпочтении.

В примерах TestEq TestEq реализует __eq__, а int не так, __eq__ вызывается оба раза (правило 1).

Но я все еще не понимаю самый первый пример с TestCmp. tc не является подклассом в int, поэтому следует вызывать AFAICT 1.__cmp__(tc), но это не так.

Ответы [ 4 ]

30 голосов
/ 17 февраля 2010

Вам не хватает ключевого исключения из обычного поведения: когда правый операнд является экземпляром подкласса класса левого операнда, сначала вызывается специальный метод для правого операнда.

См. Документацию по адресу:

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

и, в частности, следующие два абзаца:

Для объектов x и y, первый x.__op__(y) проверено. Если это не реализовано или возвращается NotImplemented, y.__rop__(x) есть пытался. Если это также не реализовано или возвращает NotImplemented, а Возникла исключительная ситуация TypeError. Но смотри следующее исключение:

Исключение из предыдущего пункта: если левый операнд является экземпляром встроенный тип или класс нового стиля, и правильный операнд является экземпляром надлежащего подкласса этого типа или класс и переопределяет базы __rop__() метод, правильный операнд __rop__() метод опробован перед левым операндом __op__() способ.

6 голосов
/ 17 февраля 2010

На самом деле в документах указано:

[__cmp__ определяется c] операциями сравнения, если расширенное сравнение (см. Выше) не определено.

__eq__ является богатым методом сравнения и, в случае TestCmp, не определен, следовательно, вызов __cmp__

1 голос
/ 17 февраля 2010

Как я знаю, __eq__() - это так называемый метод «расширенного сравнения», который вызывается для операторов сравнения вместо __cmp__() ниже. __cmp__() вызывается, если «богатое сравнение» не определено.

То есть в A == B:
Если __eq__() определено в A, оно будет называться
Остальное __cmp__() будет называться

__eq__() определено в 'str', поэтому ваша функция __cmp__() не была вызвана.

То же правило относится к __ne__(), __gt__(), __ge__(), __lt__() и __le__() методам "расширенного сравнения".

1 голос
/ 17 февраля 2010

Разве это не задокументировано в справочнике по языкам ?Если взглянуть быстро, то выглядит, что __cmp__ игнорируется, когда определены __eq__, __lt__ и т. Д.Я понимаю, что включить случай, когда __eq__ определен в родительском классе.str.__eq__ уже определено, поэтому __cmp__ в его подклассах будет игнорироваться.object.__eq__ и т. Д. Не определены, поэтому __cmp__ для его подклассов будет учитываться.

В ответ на уточненный вопрос:

Я знаю, что __eq__ вызывается в предпочтениина __cmp__, но мне непонятно, почему y.__eq__(x) вызывается предпочтительнее x.__eq__(y), когда последнее является тем, что будет происходить в состоянии документа.

Документы говорят, что x.__eq__(y) будетбыть вызванным первым, но у него есть возможность вернуть NotImplemented, в этом случае y.__eq__(x) вызывается.Я не уверен, почему вы уверены, что здесь происходит что-то другое.

В каком случае вы конкретно озадачены?Я понимаю, что вы просто озадачены делами "b" == tsc и tsc == "b", верно?В любом случае str.__eq__(onething, otherthing) вызывается.Поскольку вы не переопределяете метод __eq__ в TestStrCmp, в конечном итоге вы просто полагаетесь на метод базовой строки и говорите, что объекты не равны.

Не зная подробностей реализации str.__eq__Я не знаю, вернет ли ("b").__eq__(tsc) NotImplemented и даст ли tsc шанс обработать тест на равенство.Но даже если это так, как вы определили TestStrCmp, вы все равно получите ложный результат.

Так что не ясно, что вы видите здесь, это неожиданно.

Возможнопроисходит то, что Python предпочитает от __eq__ до __cmp__, если он определен на или сравниваемых объектов, тогда как вы ожидали, что __cmp__ для самого левого объекта будет иметь приоритет над __eq__ направый объект.Это все?

...