2016-11-24 3 views
6

Каков результат возврата NotImplemented от __eq__ специальный метод в python 3 (хорошо 3.5, если это имеет значение)?Возврат NotImplemented от __eq__

Документация нечеткая; only relevant text I found лишь отдаленно относится к «какой-либо другой запасной вариант»:

Когда NotImplemented возвращается, интерпретатор будет пытаться отраженную операцию на другом типе, или какой-либо другой запасной вариант, в зависимости от оператора. Если все предпринятые операции возвращают NotImplemented, интерпретатор поднимет соответствующее исключение. См. Implementing the arithmetic operations для получения более подробной информации.

К сожалению, в ссылке «Подробнее» не упоминается __eq__.

Мое чтение этого отрывка говорит о том, что код ниже должен поднять «соответствующее исключение», но это не делает:

class A: 
    def __eq__(self, other): 
    return NotImplemented 

class B: 
    def __eq__(self, other): 
    return NotImplemented 

# docs seems to say these lines should raise "an appropriate exception" 
# but no exception is raised 
a = A() 
b = B() 
a == b # evaluates as unequal 
a == a # evaluates as equal 

От экспериментируя, я думаю, что когда NotImplemented возвращается из __eq__, интерпретатор ведет себя как если бы __eq__ не был определен в первую очередь (в частности, он сначала свопит аргументы, и если это не решит проблему, оно сравнивается с использованием по умолчанию __eq__, который оценивает «равный», если оба объекта имеют одинаковый идентификатор) , Если это так, где в документации можно найти подтверждение этого поведения?

Edit: см Python issue 28785

+1

Чтобы повысить исключение, вам понадобится 'raise NotImplementedError' в вашем коде. – furas

+0

в моем Python 3 оцениваются как неравные. Поместите 'print (" A.eq ")' и 'print (" A.eq ")' и посмотрите, что происходит. Сначала называется «A.eq», позже «B.eq». И позже, вероятно, называется «eq» в другом типе данных, который может сравнивать его и возвращает результат - поэтому он не вызывает ошибки. Вероятно, он сравнивает «id (A())» и «id (B())». – furas

+0

'id()' может работать с любыми объектами, поэтому вы не получите ошибку. '__add__' не имеет какого-либо универсального метода, который всегда работает, поэтому он может создавать исключение. – furas

ответ

1

Не знаете, где (или если) это в документации, но основное поведение:

  • повторите операцию: __eq__(lhs, rhs)
  • , если результат не NotImplemented вернуть его
  • еще попробуйте отраженную операцию: __eq__(rhs, lhs)
  • если результат не NotImplemented вернуть его
  • в противном случае использовать соответствующее падение назад:

    eq -> те же объекты? -> Правда, иначе False

    ne -> разные предметы? Правда, еще Ложные

    многие другие -> Рейз исключение

Причина, по которой eq и ne сделать не исключений рейза:

  • они всегда могут быть определены (яблоко == оранжевый?нет)
+0

Ahh ... Хорошо, я был смущен, когда вы говорили о «одинаковых объектах» в своем комментарии. Новый (липкий) заголовок в StackOverflow запретил мне видеть, что вы отредактировали сообщение. К сожалению. – mgilson

1

== На самом деле и != проверки работа идентичны операторам сравнения заказа (< и подобных), за исключением того, что они не поднимают соответствующее исключения но отступные для сравнения идентичности. Это единственное различие.

Это легко увидеть в CPython source code. Я буду включать версию Python этого исходного кода (по крайней мере, насколько это возможно):

_mirrored_op = {'__eq__': '__eq__', # a == b => b == a 
       '__ne__': '__ne__', # a != b => b != a 
       '__lt__': '__ge__', # a < b => b >= a 
       '__le__': '__gt__', # a <= b => b > a 
       '__ge__': '__lt__', # a >= b => b < a 
       '__gt__': '__le__' # a > b => b <= a 
       } 

def richcmp(v, w, op): 
    checked_reverse = 0 
    # If the second operand is a true subclass of the first one start with 
    # a reversed operation. 
    if type(v) != type(w) and issubclass(type(w), type(v)) and hasattr(w, op): 
     checked_reverse = 1 
     res = getattr(w, _mirrored_op[op])(v)  # reversed 
     if res is not NotImplemented: 
      return res 
    # Always try the not-reversed operation 
    if hasattr(v, op): 
     res = getattr(v, op)(w)  # normal 
     if res is not NotImplemented: 
      return res 
    # If we haven't already tried the reversed operation try it now! 
    if not checked_reverse and hasattr(w, op): 
     res = getattr(w, _mirrored_op[op])(v)  # reversed 
     if res is not NotImplemented: 
      return res 
    # Raise exception for ordering comparisons but use object identity in 
    # case we compare for equality or inequality 
    if op == '__eq__': 
     res = v is w 
    elif op == '__ne__': 
     res = v is not w 
    else: 
     raise TypeError('sth') 

    return res 

и вызова a == b затем вычисляется как richcmp(a, b, '__eq__'). if op == '__eq__' - это особый случай, который возвращает a == bFalse (потому что они не идентичные объекты) и ваш a == a возвращает True (потому что они есть).

Однако поведение в Python 2.x было совершенно иным. У вас может быть до 4 (или даже 6, я не помню точно) сравнения, прежде чем возвращаться к сопоставлению идентичности!

Смежные вопросы