Development Tip

파이썬, __eq__를 기반으로 __ne __ () 연산자를 구현해야합니까?

yourdevel 2020. 10. 9. 12:29
반응형

파이썬, __eq__를 기반으로 __ne __ () 연산자를 구현해야합니까?


__eq__()연산자 를 재정의하려는 클래스가 있습니다 . __ne__()연산자도 재정의해야한다는 것이 이해가되는 것 같지만 그에 __ne__따라 구현하는 것이 합리적 __eq__입니까?

class A:
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self.__eq__(other)

아니면 파이썬이 이러한 연산자를 사용하는 방식에서 빠진 것이 있는데 이것이 좋은 생각이 아닙니다.


예, 완벽합니다. 실제로 문서 에서는 다음을 정의 __ne__할 때 정의 것을 촉구합니다 __eq__.

비교 연산자 간에는 암시 적 관계가 없습니다. 의 진실은 x==y그것이 x!=y거짓 임을 의미하지 않습니다 . 따라서 정의 할 때 연산자가 예상대로 동작하도록 __eq__()정의 __ne__()해야합니다.

많은 경우 (예 :이 경우)의 결과를 부정하는 것처럼 간단 __eq__하지만 항상 그런 것은 아닙니다.


Python, __ne__()기반으로 연산자를 구현해야 __eq__합니까?

짧은 답변 : 아니요 ==.__eq__

Python 3에서는 기본적으로 !=의 부정 ==이므로를 작성할 필요도 __ne__없으며 문서는 더 이상 작성에 대한 의견이 없습니다.

일반적으로 Python 3 전용 코드의 경우 내장 객체와 같은 부모 구현을 가리지 않으면 코드를 작성하지 마십시오.

즉, Raymond Hettinger의 의견 을 염두에 두십시오 .

__ne__메서드 는 수퍼 클래스에 아직 정의되지 않은 __eq__경우에만 자동으로 따릅니다 __ne__. 따라서 내장에서 상속하는 경우 둘 다 재정의하는 것이 가장 좋습니다.

Python 2에서 작동하는 코드가 필요한 경우 Python 2에 대한 권장 사항을 따르십시오. 그러면 Python 3에서 정상적으로 작동합니다.

파이썬 2에서 파이썬 자체가 자동으로 다른 측면에서 어떤 동작을 구현하지 않습니다 - 따라서를 정의해야 __ne__측면에서 ==대신의 __eq__. EG

class A(object):
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self == other # NOT `return not self.__eq__(other)`

증거보기

  • __ne__()기반 연산자 구현__eq__
  • __ne__Python 2에서 전혀 구현하지 않음

아래 데모에서 잘못된 동작을 제공합니다.

긴 답변

문서 파이썬 2는 말합니다 :

비교 연산자 간에는 암시 적 관계가 없습니다. 의 진실은 x==y그것이 x!=y거짓 임을 의미하지 않습니다 . 따라서 정의 할 때 연산자가 예상대로 동작하도록 __eq__()정의 __ne__()해야합니다.

, __ne__의 역으로 정의 __eq__하면 일관된 동작을 얻을 수 있습니다.

이 문서 섹션은 Python 3 용으로 업데이트되었습니다 .

기본적 으로는를 __ne__()위임 __eq__()하고 결과가 아닌 경우 결과를 반전합니다 NotImplemented.

과에서 "새로운 기능"섹션 , 우리는이 동작이 변경된 참조 :

  • !=이제를 반환 ==하지 않는 ==한의 반대를 반환합니다 NotImplemented.

구현__ne__== 을 위해 __eq__메서드를 직접 사용하는 대신 연산자 를 사용하여self.__eq__(other) 하위 클래스가 NotImplemented검사 된 유형에 대해 반환 하면 Python이 other.__eq__(self) 문서에서 다음 을 적절하게 검사 합니다 .

NotImplemented객체

이 유형에는 단일 값이 있습니다. 이 값을 가진 단일 개체가 있습니다. 이 개체는 기본 제공 이름을 통해 액세스됩니다 NotImplemented. 숫자 메서드 및 풍부한 비교 메서드는 제공된 피연산자에 대한 연산을 구현하지 않는 경우이 값을 반환 할 수 있습니다. (그런 다음 인터프리터는 연산자에 따라 반영된 작업 또는 다른 폴백을 시도합니다.) 진실 값은 참입니다.

그들은, 파이썬 검사 동일한 형태가 아닌 경우 풍부한 비교 연산자가 주어진 경우,이 경우 other서브 타입이며,이 정의 된 연산자를 갖는 경우, 사용 other(위한 역 제의 방법을 <, <=, >=>). 경우 NotImplemented반환, 다음 은 그 반대의 방법을 사용합니다. ( 동일한 방법을 두 번 확인 하지 않습니다 .) ==연산자를 사용하면이 논리가 발생할 수 있습니다.


기대

의미 론적 __ne__으로는 클래스의 사용자가 A.의 모든 인스턴스에 대해 다음 함수가 동일 할 것으로 기대하므로 동등성 검사 측면에서 구현해야합니다 .

def negation_of_equals(inst1, inst2):
    """always should return same as not_equals(inst1, inst2)"""
    return not inst1 == inst2

def not_equals(inst1, inst2):
    """always should return same as negation_of_equals(inst1, inst2)"""
    return inst1 != inst2

즉, 위의 두 함수는 항상 동일한 결과를 반환 해야합니다 . 그러나 이것은 프로그래머에 따라 다릅니다.

다음을 __ne__기반으로 정의 할 때 예상치 못한 동작 증명 __eq__:

먼저 설정 :

class BaseEquatable(object):
    def __init__(self, x):
        self.x = x
    def __eq__(self, other):
        return isinstance(other, BaseEquatable) and self.x == other.x

class ComparableWrong(BaseEquatable):
    def __ne__(self, other):
        return not self.__eq__(other)

class ComparableRight(BaseEquatable):
    def __ne__(self, other):
        return not self == other

class EqMixin(object):
    def __eq__(self, other):
        """override Base __eq__ & bounce to other for __eq__, e.g. 
        if issubclass(type(self), type(other)): # True in this example
        """
        return NotImplemented

class ChildComparableWrong(EqMixin, ComparableWrong):
    """__ne__ the wrong way (__eq__ directly)"""

class ChildComparableRight(EqMixin, ComparableRight):
    """__ne__ the right way (uses ==)"""

class ChildComparablePy3(EqMixin, BaseEquatable):
    """No __ne__, only right in Python 3."""

동일하지 않은 인스턴스를 인스턴스화합니다.

right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)

예상되는 동작 :

(참고 : 아래 각 항목의 모든 두 번째 주장은 동일하므로 이전 항목과 논리적으로 중복되지만 하나가 다른 항목의 하위 클래스 일 때 순서가 중요하지 않음 을 보여주기 위해 포함합니다 . )

이러한 인스턴스는 다음으로 __ne__구현되었습니다 ==.

assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1

Python 3에서 테스트하는 이러한 인스턴스도 올바르게 작동합니다.

assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1

그리고 이것들이 다음과 같이 __ne__구현 되었음을 __eq__상기하십시오. 이것은 예상되는 동작이지만 구현은 올바르지 않습니다.

assert not wrong1 == wrong2         # These are contradicted by the
assert not wrong2 == wrong1         # below unexpected behavior!

예상치 못한 동작 :

이 비교는 위의 비교 ( not wrong1 == wrong2) 와 모순 됩니다.

>>> assert wrong1 != wrong2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

과,

>>> assert wrong2 != wrong1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

__ne__Python 2에서 건너 뛰지 마십시오.

__ne__Python 2에서 구현 건너 뛰지 않아야한다는 증거는 다음과 같은 동등한 객체를 참조하세요.

>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True

위의 결과는 False!

Python 3 소스

에 대한 기본 CPython 구현 __ne__은 다음 위치 typeobject.c에 있습니다object_richcompare .

    case Py_NE:
        /* By default, __ne__() delegates to __eq__() and inverts the result,
           unless the latter returns NotImplemented. */
        if (self->ob_type->tp_richcompare == NULL) {
            res = Py_NotImplemented;
            Py_INCREF(res);
            break;
        }
        res = (*self->ob_type->tp_richcompare)(self, other, Py_EQ);
        if (res != NULL && res != Py_NotImplemented) {
            int ok = PyObject_IsTrue(res);
            Py_DECREF(res);
            if (ok < 0)
                res = NULL;
            else {
                if (ok)
                    res = Py_False;
                else
                    res = Py_True;
                Py_INCREF(res);
            }
        }

여기서 우리는

하지만 기본값 __ne____eq__?

__ne__C 수준에서 Python 3의 기본 구현 세부 사항 __eq__은 더 높은 수준 ==( PyObject_RichCompare )이 덜 효율적이므로 NotImplemented.

경우 __eq__올바르게 구현, 다음의 부정은 ==또한 올 - 그것은 우리가 우리의 낮은 수준의 구현 세부 사항을 피할 수 있습니다 __ne__.

사용은 ==우리의 낮은 수준의 논리를 유지하기 위해 우리를 허용 곳, 그리고 주소 NotImplemented__ne__.

==반환 될 수 있다고 잘못 가정 할 수 있습니다 NotImplemented.

실제로 __eq__신원을 확인하는 의 기본 구현과 동일한 논리를 사용합니다 (아래 do_richcompare 및 증거 참조).

class Foo:
    def __ne__(self, other):
        return NotImplemented
    __eq__ = __ne__

f = Foo()
f2 = Foo()

그리고 비교 :

>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True

공연

내 말을 믿지 말고 더 성능이 좋은 것을 보자.

class CLevel:
    "Use default logic programmed in C"

class HighLevelPython:
    def __ne__(self, other):
        return not self == other

class LowLevelPython:
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

def c_level():
    cl = CLevel()
    return lambda: cl != cl

def high_level_python():
    hlp = HighLevelPython()
    return lambda: hlp != hlp

def low_level_python():
    llp = LowLevelPython()
    return lambda: llp != llp

이러한 성능 수치가 스스로를 대변한다고 생각합니다.

>>> import timeit
>>> min(timeit.repeat(c_level()))
0.09377292497083545
>>> min(timeit.repeat(high_level_python()))
0.2654011140111834
>>> min(timeit.repeat(low_level_python()))
0.3378178110579029

이것은 low_level_python그렇지 않으면 C 레벨에서 처리 될 파이썬에서 로직을 수행 한다고 생각할 때 의미가 있습니다 .

일부 비평가에 대한 응답

다른 답변자는 다음과 같이 씁니다.

Aaron Hall의 메서드 구현 not self == other( is ) __ne__절대 반환 할 수 없기 때문에 올바르지 않으므로 우선 순위가 있는 메서드는 우선 순위가없는 메서드로 대체 될 수 없습니다 .NotImplementednot NotImplementedFalse__ne____ne__

__ne__결코 돌아 가기 NotImplemented가 잘못된하지 않습니다. 대신 NotImplemented.NET과의 동등성 검사를 통해 우선 순위를 처리 ==합니다. ==올바르게 구현 되었다고 가정하면 완료됩니다.

not self == other이전에는 __ne__메서드 의 기본 Python 3 구현 이었지만 버그였으며 ShadowRanger가 발견 한대로 2015 년 1 월 Python 3.4에서 수정되었습니다 (문제 # 21408 참조).

글쎄, 이것을 설명합시다.

앞에서 언급했듯이 Python 3은 기본적 __ne__으로 먼저 self.__eq__(other)반환 여부 NotImplemented(싱글 톤)를 확인하여 처리 is합니다. 다음은 클래스 믹스 인으로 작성된 논리입니다.

class CStyle__ne__:
    """Mixin that provides __ne__ functionality equivalent to 
    the builtin functionality
    """
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

이것은 C 레벨 Python API의 정확성을 위해 필요하며 Python 3에서 도입되어

불필요한. __ne__자체 검사를 구현하는 방법 __eq__직접 또는 통해 위임하는 방법을 포함하여 모든 관련 방법이 제거 되었으며 가장 일반적인 방법이었습니다.====

결론

Python 2 호환 코드의 경우를 사용 ==하여 __ne__. 더 많은 것 :

  • 옳은
  • 단순한
  • 공연자

Python 3에서만 C 수준에서 저수준 부정을 사용합니다. 훨씬 간단하고 성능이 뛰어납니다 (프로그래머가 올바른지 판단 할 책임이 있습니다 ).

다시 말하지만, 고수준 Python에서 저수준 논리를 작성 하지 마십시오 .


기록을 위해 정규적으로 정확하고 교차하는 Py2 / Py3 휴대용 장치 __ne__는 다음과 같습니다.

import sys

class ...:
    ...
    def __eq__(self, other):
        ...

    if sys.version_info[0] == 2:
        def __ne__(self, other):
            equal = self.__eq__(other)
            return equal if equal is NotImplemented else not equal

이것은 __eq__정의 할 수 있는 모든 항목에서 작동 합니다.

  • 달리 not (self == other)관련된 클래스 중 하나의 결과는 것을 의미하지 않는 경우, 비교를 포함하는 몇 가지 성가신 / 복잡한 경우에 간섭하지 않는 __ne__결과와 동일한 not__eq__(예를 들면 SQLAlchemy의의 ORM, 모두 __eq____ne__특별한 프록시 객체 반환 하지 TrueFalse, 그리고에 노력 not의 결과를 __eq__반환 False하지 않고) 올바른 프록시 객체보다.
  • 와 달리 not self.__eq__(other), 이것은 반환 __ne__할 때 다른 인스턴스 올바르게 위임 self.__eq__합니다 NotImplemented( not self.__eq__(other)사실이기 때문에 추가로 잘못 될 수 NotImplemented있으므로 __eq__비교를 수행하는 방법을 알지 못했을 때를 __ne__반환합니다 False. 이는 실제로 유일한 경우 두 객체가 동일하다는 것을 의미합니다. 요청 된 개체는 전혀 모름, 같지 않음의 기본값을 의미 함)

당신이 경우 __eq__사용하지 않는 NotImplemented반환, (의미 오버 헤드)이 작품은 사용 않는 경우 NotImplemented제대로 때때로,이 핸들을. 그리고 Python 버전 확인은 클래스가 importPython 3에서 -ed 인 경우 __ne__정의되지 않은 상태로 유지되어 Python의 기본적이고 효율적인 폴백 __ne__구현 (위의 C 버전) 이 대신 할 수 있음을 의미합니다.


이것이 필요한 이유

Python 오버로딩 규칙

다른 솔루션 대신이 작업을 수행하는 이유에 대한 설명은 다소 난해합니다. Python에는 연산자 오버로딩과 특히 비교 연산자에 대한 몇 가지 일반적인 규칙이 있습니다.

  1. (모든 연산자에 적용됨)를 실행할 때 LHS OP RHS, try LHS.__op__(RHS),이 값이 반환 NotImplemented되면 try RHS.__rop__(LHS). 예외 : RHS이 클래스의 하위 클래스 인 경우 먼저LHS 테스트하십시오 . 비교 연산자의 경우, 그리고 자신의 'ROP'의이다 (대한 테스트 순서가 그래서 입니다 , 다음 경우, 반전 의 서브 클래스 의 클래스)RHS.__rop__(LHS) __eq____ne____ne__LHS.__ne__(RHS)RHS.__ne__(LHS)RHSLHS
  2. "교환 된"연산자의 개념을 제외하고 연산자간에 암시적인 관계가 없습니다. 동일한 클래스의 경우에도 LHS.__eq__(RHS)반환 TrueLHS.__ne__(RHS)반환을 의미하지 않습니다 False(사실 연산자는 부울 값을 반환 할 필요도 없습니다. SQLAlchemy와 같은 ORM은 의도적으로 그렇지 않아보다 표현적인 쿼리 구문을 허용합니다). Python 3부터 기본 __ne__구현은 이러한 방식으로 작동하지만 계약이 아닙니다. __ne__.NET의 엄격한 반대가 아닌 방식으로 재정의 할 수 있습니다 __eq__.

이것이 비교기 과부하에 적용되는 방법

따라서 연산자를 오버로드하면 두 가지 작업이 있습니다.

  1. 작업을 직접 구현하는 방법을 알고 있는 경우 비교를 수행하는 방법에 대한 자신의 지식 사용하여 수행하십시오 (암시 적 또는 명시 적으로 작업의 다른쪽에 위임하지 마십시오. 그렇게하면 부정확성 및 / 또는 무한 재귀 위험이 있습니다. 당신이 그것을하는 방법에 따라)
  2. 당신이 경우 하지 않는 동작 자신을 구현하는 방법을 알고, 항상 반환 NotImplemented파이썬은 다른 피연산자의 구현에 위임 할 수 있도록,

문제 not self.__eq__(other)

def __ne__(self, other):
    return not self.__eq__(other)

절대 다른쪽에 위임하지 않습니다 ( __eq__올바르게 반환하는 경우 올바르지 않음 NotImplemented). self.__eq__(other)반환 NotImplemented( "truthy"입니다), 당신은 자동으로 반환 False, 그래서 A() != something_A_knows_nothing_about반환 False, 그것은 확인해야 할 때 경우 something_A_knows_nothing_about의 인스턴스를 비교하는 방법을 알고 A, 그것을하지 않는 경우, 그것은 반환해야 True어느 쪽이 방법을 알고 있다면 때문에 ( 다른 것과 비교하면 서로 같지 않은 것으로 간주됩니다). A.__eq__이 잘못 구현 된 경우 ( 다른 쪽을 인식하지 못할 때 False대신 반환 NotImplemented) 이는 A의 관점 에서 "올바른" 반환입니다 True( A동등하다고 생각하지 않기 때문에 동일하지 않음). ~에서 잘못something_A_knows_nothing_about의 관점은 결코 묻지도 않았기 때문에 something_A_knows_nothing_about; A() != something_A_knows_nothing_aboutTrue하지만, something_A_knows_nothing_about != A()False, 또는 다른 반환 값.

문제 not self == other

def __ne__(self, other):
    return not self == other

더 미묘합니다. __ne__의 논리적 역인 모든 클래스를 포함하여 클래스의 99 %에 대해 정확할 것 입니다 __eq__. 그러나 not self == other클래스의 수단 규칙 모두 위에서 언급 한 휴식, __ne__ 아니다 의 논리 역 __eq__이 구현할 수있는 경우 피연산자 중 하나가 요구하지 않기 때문에, 결과는 다시 한번 비 재귀있는 __ne__모든에서도 다른 경우, 피연산자는 할 수 없습니다. 가장 간단한 예는 이상한 클래스 반환 False에 대한 모든 비교는, 이렇게 A() == Incomparable()하고 A() != Incomparable()모두 반환 False. A.__ne__( NotImplemented비교를 수행하는 방법을 모를 때 반환 되는) 올바른 구현을 통해 관계는 재귀 적입니다. A() != Incomparable()Incomparable() != A()결과에 동의합니다 (전자의 경우를 A.__ne__반환 NotImplementedIncomparable.__ne__다음를 반환 False하고 후자의 경우 직접 Incomparable.__ne__반환하기 때문입니다 False). 그러나 경우 A.__ne__로 구현됩니다 return not self == other, A() != Incomparable()반환 True(때문에 A.__eq__반환하지 NotImplementedIncomparable.__eq__반환 False하고, A.__ne__반전 것과 True) 동안 Incomparable() != A()반환False.

여기 에서이 작업의 예를 볼 수 있습니다 .

물론, 항상 반환하는 클래스 False모두 __eq____ne__조금 이상하다. 그러나 앞에서 언급 한로 __eq____ne__도 반환 할 필요가 없습니다 True/ False; SQLAlchemy의 ORM은 비교기와 클래스가 그 반환 쿼리 작성을위한 특별한 프록시 객체가 아닌 True/ False모든 (그들이있는 거 "truthy"경우 부울 맥락에서 평가하지만,이 같은 맥락에서 평가 안되는)에서.

과부하에 실패하여 __ne__제대로하면 됩니다 코드로, 그런 종류의 클래스를 휴식 :

 results = session.query(MyTable).filter(MyTable.fieldname != MyClassWithBadNE())

작동합니다 (SQLAlchemy가 MyClassWithBadNESQL 문자열에 삽입하는 방법을 전혀 알고 있다고 가정 합니다.이 작업 MyClassWithBadNE은 전혀 협력 필요 없이 유형 어댑터로 수행 할 수 있음 ). 예상되는 프록시 객체를에 전달하는 filter동안

 results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)

전달 끝날 것 filter일반을 False하기 때문에, self == other반환 프록시 객체, 그리고 not self == other단지에 truthy 프록시 개체를 변환합니다 False. 바라 건데, filter같이 유효하지 않은 인수를 처리되고에서 예외가 발생합니다 False. 많은 사람들이 비교의 왼쪽에 일관 MyTable.fieldname 되어야 한다고 주장 할 것이라고 확신하지만 , 사실은 일반적인 경우에 이것을 시행 할 프로그래밍 적 이유가 없으며 올바른 제네릭 __ne__은 어느 쪽이든 return not self == other작동 하지만 작동 하는 경우에만 작동합니다. 하나의 배열로.


짧은 대답 : 예 (하지만 올바르게 수행하려면 설명서를 읽으십시오)

ShadowRanger의 __ne__메서드 구현은 올바른 것입니다 (기본 Python 3 구현과 똑같이 작동한다는 의미에서).

def __ne__(self, other):
    result = self.__eq__(other)

    if result is not NotImplemented:
        return not result

    return NotImplemented

Aaron Hall의 메서드 구현 not self == other( is ) __ne__절대 반환 할 수 없기 때문에 올바르지 않으므로 우선 순위가 있는 메서드는 우선 순위가없는 메서드로 대체 될 수 없습니다 . 이전에는 메서드 의 기본 Python 3 구현 이었지만 버그였으며 ShadowRanger가 발견 한대로 2015 년 1 월 Python 3.4에서 수정되었습니다 ( 문제 # 21408 참조 ).NotImplementednot NotImplementedFalse__ne____ne__not self == other__ne__

비교 연산자의 구현

파이썬 언어 참조 파이썬에 대한 3 주 장 III 데이터 모델 :

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

이것이 소위“풍부한 비교”방법입니다. 연산자 기호와 메소드 이름 간의 대응은 다음과 같습니다. x<ycalls x.__lt__(y), x<=ycalls x.__le__(y), x==ycalls x.__eq__(y), x!=ycalls x.__ne__(y), x>ycalls x.__gt__(y)x>=ycalls x.__ge__(y).

풍부한 비교 메서드는 NotImplemented주어진 인수 쌍에 대한 연산을 구현하지 않으면 싱글 톤을 반환 할 수 있습니다 .

이러한 메서드의 인수가 교체 된 버전은 없습니다 (왼쪽 인수가 작업을 지원하지 않지만 오른쪽 인수가 지원할 때 사용됨). 오히려, __lt__()그리고 __gt__()서로의 반영이다, __le__()그리고 __ge__()서로의 반사하고, __eq__()그리고 __ne__()자신의 반영이다. 피연산자가 다른 유형이고 오른쪽 피연산자의 유형이 왼쪽 피연산자 유형의 직접 또는 간접 서브 클래스 인 경우 오른쪽 피연산자의 반영된 메소드가 우선 순위를 가지며 그렇지 않으면 왼쪽 피연산자의 메소드가 우선 순위를 갖습니다. 가상 서브 클래 싱은 고려되지 않습니다.

이것을 파이썬 코드로 번역하면 ( operator_eqfor ==, operator_nefor !=, operator_ltfor <, operator_gtfor >, operator_lefor <=operator_gefor 사용 >=) :

def operator_eq(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__eq__(left)

        if result is NotImplemented:
            result = left.__eq__(right)
    else:
        result = left.__eq__(right)

        if result is NotImplemented:
            result = right.__eq__(left)

    if result is NotImplemented:
        result = left is right

    return result


def operator_ne(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ne__(left)

        if result is NotImplemented:
            result = left.__ne__(right)
    else:
        result = left.__ne__(right)

        if result is NotImplemented:
            result = right.__ne__(left)

    if result is NotImplemented:
        result = left is not right

    return result


def operator_lt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__gt__(left)

        if result is NotImplemented:
            result = left.__lt__(right)
    else:
        result = left.__lt__(right)

        if result is NotImplemented:
            result = right.__gt__(left)

    if result is NotImplemented:
        raise TypeError(f"'<' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_gt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__lt__(left)

        if result is NotImplemented:
            result = left.__gt__(right)
    else:
        result = left.__gt__(right)

        if result is NotImplemented:
            result = right.__lt__(left)

    if result is NotImplemented:
        raise TypeError(f"'>' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_le(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ge__(left)

        if result is NotImplemented:
            result = left.__le__(right)
    else:
        result = left.__le__(right)

        if result is NotImplemented:
            result = right.__ge__(left)

    if result is NotImplemented:
        raise TypeError(f"'<=' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_ge(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__le__(left)

        if result is NotImplemented:
            result = left.__ge__(right)
    else:
        result = left.__ge__(right)

        if result is NotImplemented:
            result = right.__le__(left)

    if result is NotImplemented:
        raise TypeError(f"'>=' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result

비교 방법의 기본 구현

문서는 다음을 추가합니다.

기본적 으로는를 __ne__()위임 __eq__()하고 결과가 아닌 경우 결과를 반전합니다 NotImplemented. 비교 연산자 간에는 다른 암시 적 관계 (x<y or x==y)가 없습니다 x<=y. 예를 들어의 진실은 의미하지 않습니다 .

비교 방법의 기본 구현 ( __eq__, __ne__, __lt__, __gt__, __le__및은 __ge__) 이에 의해 주어질 수있다 :

def __eq__(self, other):
    return NotImplemented

def __ne__(self, other):
    result = self.__eq__(other)

    if result is not NotImplemented:
        return not result

    return NotImplemented

def __lt__(self, other):
    return NotImplemented

def __gt__(self, other):
    return NotImplemented

def __le__(self, other):
    return NotImplemented

def __ge__(self, other):
    return NotImplemented

따라서 이것이 __ne__방법 의 올바른 구현입니다 . 그리고 그것은 항상의 역 반환하지 않습니다 __eq__때 때문에 방법을 __eq__메서드가 반환 NotImplemented, 그 역이 not NotImplemented있습니다 False(같은 bool(NotImplemented)것입니다 True) 대신 원하는 중 NotImplemented.

잘못된 구현 __ne__

Aaron Hall이 위에서 설명한 것처럼 메서드 not self.__eq__(other)의 기본 구현은 아닙니다 __ne__. 하지만 not self == other. 후자는 not self == other두 가지 경우에 기본 구현의 동작과 구현의 동작을 비교하여 아래에 설명되어 있습니다.

  • __eq__메소드가 리턴 NotImplemented;
  • __eq__메서드는와 다른 값을 반환합니다 NotImplemented.

기본 구현

A.__ne__메서드가 기본 구현을 사용하고 A.__eq__메서드가 NotImplemented다음을 반환 할 때 어떤 일이 발생하는지 살펴 보겠습니다 .

class A:
    pass


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) == "B.__ne__"
  1. !=전화 A.__ne__.
  2. A.__ne__전화 A.__eq__.
  3. A.__eq__를 반환합니다 NotImplemented.
  4. !=전화 B.__ne__.
  5. B.__ne__를 반환합니다 "B.__ne__".

때이 쇼 A.__eq__방법 반환 NotImplementedA.__ne__방법은 다시에 내리는 B.__ne__방법.

이제 A.__ne__메서드가 기본 구현을 사용하고 메서드가 다음과 A.__eq__다른 값을 반환 할 때 어떤 일이 발생하는지 살펴 보겠습니다 NotImplemented.

class A:

    def __eq__(self, other):
        return True


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is False
  1. !=전화 A.__ne__.
  2. A.__ne__전화 A.__eq__.
  3. A.__eq__를 반환합니다 True.
  4. !=반환 not True, 즉 False.

이것은이 경우 A.__ne__메서드가 메서드의 역을 반환 함을 보여줍니다 A.__eq__. 따라서이 __ne__메서드는 문서에 광고 된 것처럼 작동합니다.

A.__ne__위에 제공된 올바른 구현으로 메서드 의 기본 구현을 재정의하면 동일한 결과가 생성됩니다.

not self == other 이행

A.__ne__메서드 의 기본 구현을 구현으로 재정의 not self == other하고 A.__eq__메서드가 NotImplemented다음을 반환 할 때 어떤 일이 발생하는지 살펴 보겠습니다 .

class A:

    def __ne__(self, other):
        return not self == other


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is True
  1. !=전화 A.__ne__.
  2. A.__ne__전화 ==.
  3. ==전화 A.__eq__.
  4. A.__eq__를 반환합니다 NotImplemented.
  5. ==전화 B.__eq__.
  6. B.__eq__를 반환합니다 NotImplemented.
  7. ==반환 A() is B(), 즉 False.
  8. A.__ne__반환 not False, 즉 True.

The default implementation of the __ne__ method returned "B.__ne__", not True.

Now let’s see what happens when overriding the default implementation of the A.__ne__ method with the not self == other implementation and the A.__eq__ method returns a value different from NotImplemented:

class A:

    def __eq__(self, other):
        return True

    def __ne__(self, other):
        return not self == other


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is False
  1. != calls A.__ne__.
  2. A.__ne__ calls ==.
  3. == calls A.__eq__.
  4. A.__eq__ returns True.
  5. A.__ne__ returns not True, that is False.

The default implementation of the __ne__ method also returned False in this case.

Since this implementation fails to replicate the behavior of the default implementation of the __ne__ method when the __eq__ method returns NotImplemented, it is incorrect.


If all of __eq__, __ne__, __lt__, __ge__, __le__, and __gt__ make sense for the class, then just implement __cmp__ instead. Otherwise, do as you're doing, because of the bit Daniel DiPaolo said (while I was testing it instead of looking it up ;) )

참고URL : https://stackoverflow.com/questions/4352244/python-should-i-implement-ne-operator-based-on-eq

반응형