Подтвердить что ты не робот

Почему/когда в Python выполняет `x == y` call` y.__ eq __ (x) `?

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

Изменить: просто чтобы уточнить, я знаю, что __eq__ вызывается в preferecne для __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

Отредактируйте: От Марка Дикинсона ответ и комментарий, это будет выглядеть так:

  • Богатые переопределения сравнения __cmp__
  • __eq__ является ли он __rop__ для него __op__ (и аналогичен для __lt__, __ge__ и т.д.)
  • Если левый объект является классом builtin или new-style, а правым является его подкласс, правый объект __rop__ проверяется перед левым объектом __op__

Это объясняет поведение в примерах TestStrCmp. TestStrCmp является подклассом str, но не реализует его собственный __eq__, поэтому __eq__ of 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) должен вызываться, но это не так.

4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

Собственно, в docs говорится:

[__cmp__ c] используется для операций сравнения, если богатое сравнение (см. выше) не определено.

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

Ответ 3

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

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

Я знаю, что __eq__ вызывается в preferecne до __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__ справа объект. Это так?

Ответ 4

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

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

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

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