Почему NotImplemented не вызывает TypeError?

Предположим, я определяю класс A и не хочу, чтобы кто-нибудь написал неравенство этого класса, не ускользнув.

class A():
    def __ne__(self, other):
        return NotImplemented
print(A() != A())

Но это выводит True и не вызывает TypeError, хотя я намеренно «отключил» оператор !=?


person Turion    schedule 22.11.2012    source источник


Ответы (2)


Когда вы возвращаете NotImplemented, вы указываете, что не знаете, должно ли __ne__ возвращать True или False.

Обычно Python меняет местами операнды; если a != b приводит к NotImplemented, он попытается вместо этого b != a. Здесь тоже ничего не получится, так как вы используете один и тот же тип с обеих сторон оператора. Для оператора != Python затем вернется к сравнению своих адресов памяти, и они не совпадают (два отдельных экземпляра), поэтому возвращается False.

Дополнительные сведения см. в do_richcompare функции C.

Вам придется поднять TypeError() вручную, если это ваш ожидаемый результат.

person Martijn Pieters    schedule 22.11.2012
comment
Простота реализации расширенного сравнения 3.x является долгожданным облегчением. В 2.x вместо двойного вызова __ne__ Python вызывает его 6 раз (дважды, когда slot_tp_richcompare вызывается PyObject_RichCompare, затем еще 4 раза, когда try_rich_compare пытается в обычном порядке и в порядке замены). Наконец, дело доходит до сравнения указателей в default_3way_compare, сложного из-за необходимости приведения к uintptr_t для поддержки упорядочения. - person Eryk Sun; 23.11.2012
comment
Да, я был очень рад увидеть тег python-3.x в этом вопросе. Необходимость вдаваться в подробности о том, как все это работает в Python 2, привело бы к немного более длинному ответу. Однако я насчитал только 4 вызовов __ne__ для A() != A() в порядке LR, RL, RL, LR (для левого и правого экземпляров). - person Martijn Pieters; 23.11.2012

Это дает вам True, потому что вы возвращаете исключение, а не вызываете его. Это означает, что вы возвращаете ненулевой объект (исключение) в результате теста. Объекты Non-Null оцениваются как True, если не указано иное. Помните, что исключение является обычным объектом, пока вы его не вызовете.

поэтому у вас должен быть метод, похожий на этот:

class A():
    def __ne__(self, other):
        raise NotImplementedError
person EnricoGiampieri    schedule 22.11.2012
comment
Нет, это не исключение. Прочтите документацию для __ne__; это синглтон только для этой цели. - person Martijn Pieters; 22.11.2012
comment
Правда, я думал об исключении. Не поймите меня неправильно, но документация в этом смысле несовершенна, так как bool() в NotImplemented возвращает True и не дает какой-либо значимой ошибки. Итак, правильная процедура - вызвать исключение, по крайней мере, в намерении ОП (насколько я могу это понять) - person EnricoGiampieri; 22.11.2012
comment
Нет, NotImplemented — очень полезное сигнальное значение (то есть значение, указывающее, что для чего-то нет реализации). - person ; 22.11.2012
comment
NotImplemented — это специальный синглтон, используемый для такого рода контекста. Код сравнения Python специально тестирует его. Я чувствую, что специальное исключение, возможно, было бы лучшим выбором (например, StopIteration), но я думаю, что это пережиток самых ранних дней Python. Однако, вероятно, есть пограничные случаи, когда исключение не является подходящим ответом. Есть и другие варианты использования NotImplemented (и эквивалента C API Py_NotImplemented), например, для числового преобразования. - person Martijn Pieters; 22.11.2012