IT

__ne__을 __eq__의 부정으로 구현해야 합니까?

itgroup 2023. 10. 10. 20:14
반응형

__ne__을 __eq__의 부정으로 구현해야 합니까?

는 하고 싶은 이 있습니다.__eq__방법. 내가 그 일을 무시해야 한다는 것이 말이 되는 것 같습니다무시하는 게 할 것 요.__ne__방법. 구현해야 합니까해야 요?__ne____eq__아니면 나쁜 생각입니까?

class A:

    def __init__(self, state):
        self.state = state

    def __eq__(self, other):
        return self.state == other.state

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

을 요?__ne__()n__eq__?

단답형: 해야 한다면 구현하지 마십시오사용하세요. 하지만 필요한 경우 사용하십시오.==,것은 아니다.__eq__

파이썬 3에서는.!=입니다.==.다를은하십시오.__ne__더 를 에 대해 을 제시하지

일반적으로 Python 3 전용 코드의 경우 기본 제공 개체의 경우와 같이 상위 구현을 무색하게 할 필요가 없는 한 작성하지 마십시오.

Raymond Hettinger가 을 명심해야 합니다.

__ne__다에서 자동으로 .__eq__만일의 경우에만__ne__아직 슈퍼클래스에 정의되어 있지 않습니다.기본 제공에서 상속하는 경우 둘 다 무시하는 것이 좋습니다.

Python 2에서 작동하려면 Python 2의 권장 사항을 따라가면 Python 3에서 작동합니다.

는 다른 2 에서인 를 __ne__에서로 ==__eq__를 들면 .

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__()n__eq__그리고.
  • 하지 않는__ne__ 2 파이썬 에서.

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

긴 대답

파이썬 2의 설명서에는 다음과 같이 나와 있습니다.

비교 연산자 간에 내포된 관계가 없습니다.실의 .x==y다를 는 않습니다.x!=y거짓입니다.따라서, 다음과 같이 정의할 때__eq__().__ne__()조작자가 예상대로 행동하도록 하기 위해서입니다.

입니다.__ne____eq__는 일관된 할 수 , .

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

으로,__ne__()__eq__()시킵니다.NotImplemented.

"새로운 기능" 섹션에서는 이러한 동작이 변경되었음을 확인할 수 있습니다.

  • !=다의 합니다.==,~하지 않는 한==sNotImplemented.

구현을 위해, 우리는 연산자를 사용하는 대신에__eq__self.__eq__(other)NotImplemented은 확인된 에 대해입니다를 확인할 것입니다.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__ 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!

파이썬 3 소스

CPython에 구현:__ne__ 있습니다.

case Py_NE:
    /* By default, __ne__() delegates to __eq__() and inverts the result,
       unless the latter returns NotImplemented. */
    if (Py_TYPE(self)->tp_richcompare == NULL) {
        res = Py_NotImplemented;
        Py_INCREF(res);
        break;
    }
    res = (*Py_TYPE(self)->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);
        }
    }
    break;

은 ㅇ__ne__사용하다__eq__?

3__ne__ 사항은 C를 합니다.__eq__ 레벨이e이기 때문에==(PyObject_RichCompare)는 효율성이 떨어지므로 처리해야 합니다.NotImplemented.

한다면__eq__음,합니다.==또한 합니다에서 의 구현 수 . 이를 통해 우리는 낮은 수준의 구현 세부 사항을 피할 수 있습니다.__ne__.

으로.==우리의 낮은 수준의 논리를 한 곳에 유지하고, 주소 지정을 피할 수 있게 해줍니다.NotImplemented인에__ne__.

는 그 를 도 있습니다.==돌아올지도 모릅니다NotImplemented.

는 합니다의 합니다.__eq__, 동일성을 확인하는 것(아래의 do_rich 비교 및 우리의 증거 참조)

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 레벨에서 처리될 파이썬에서 논리를 수행하고 있습니다.

일부 비평가에 대한 대응

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

방식not self == other__ne__가 올바르지 .를(를) 반환할 수 .NotImplemented(not NotImplemented이다.False 및 ) __ne__ 순위가 수 .__ne__우선 순위가 없는 메서드입니다.

있는 것.__ne__ 돌아오지 않는rNotImplemented틀리지 않습니다.에,다를 지정을 합니다.NotImplemented==합니다.==정확하게 구현되면 우린 끝입니다

not self == other 3 3 이었습니다.__ne__메서드는 버그였고 ShadowRanger가 알아차린 대로 2015년 1월 파이썬 3.4에서 수정되었습니다(호 #21408 참조).

그럼 설명을 해보죠.

으로 3 입니다를 __ne__self.__eq__(other)sNotImplemented - 톤)와 함 -다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에 도입되어 제작되었습니다.

잉여 relevant__ne__합니다,되었습니다에 을 포함하여되었습니다.__eq__ 또는 ==- 그리고==가장 일반적인 방법이었습니다.

대칭성이 중요할까요?

의 집요한 는 합니다를 를 제공합니다.NotImplemented인에__ne__하게 여기는 건합니다입니다명확한 예를 들어 논의를 강화해 보겠습니다.

class B:
    """
    this class has no __eq__ implementation, but asserts 
    any instance is not equal to any other object
    """
    def __ne__(self, other):
        return True

class A:
    "This class asserts instances are equivalent to all other objects"
    def __eq__(self, other):
        return True

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, False, True)

이 를 __ne__ Python

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        result = other.__eq__(self)
        if result is NotImplemented:
            return NotImplemented
        return not result

>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, True, True)

분명히 우리는 이 사례들이 동등하고 동등하지 않다는 것을 신경쓰지 말아야 합니다.

저는 대칭성이 합리적인 코드의 가정보다 덜 중요하고 문서의 조언을 따를 것을 제안합니다.

가 A 을 했다면,__eq__서 내 수 입니다.

class B:
    def __ne__(self, other):
        return True

class A:
    def __eq__(self, other):
        return False         # <- this boolean changed... 

>>> A() == B(), B() == A(), A() != B(), B() != A()
(False, False, True, True)

결론

2 Python 2 합니다를 ==__ne__ 그 이상:

  • 맞아요.
  • 간단하죠.
  • 수행적인

Python 3에서만 C 레벨의 하위 레벨 부정을 사용하십시오. 이는 훨씬 더 단순하고 성능이 뛰어납니다(프로그래머가 정확하다고 판단할 책임이 있지만).

다시 말하지만, 상위 파이썬에서 낮은 수준의 논리를 쓰지 마세요.

네, 괜찮습니다.사실, 문서는 당신이 다음과 같이 정의할 것을 촉구합니다.__ne__할 때__eq__:

비교 연산자 간에 내포된 관계가 없습니다.실의 .x==y다를 는 않습니다.x!=y거짓입니다.따라서, 다음과 같이 정의할 때__eq__().__ne__()조작자가 예상대로 행동하도록 하기 위해서입니다.

), 입니다(), 의 입니다.__eq__, 그러나 항상은 아닙니다.

, 된 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__않음True아니면False하려고, ㅇnot__eq__입니다.False , ).
  • 와 다르게not self.__eq__(other)를 통해 로 위임합니다.__ne__self.__eq__sNotImplemented(not self.__eq__(other)도 있어요,eNotImplemented이니까할 제.__eq__ 할지고__ne__입니다.False을 받은 때 두 개체가 하며, 합니다).

에 당신의 ㅇ__eq__않는NotImplemented는 ( 없는 returns,은께)합니다,면을 .NotImplemented때때로, 이것은 그것을 적절하게 처리합니다.import -python 3에서 에서,__ne__는 정의되지 않은 상태로 남겨져 파이썬의 네이티브 효율적인 폴백 구현(위의 C 버전)을 대신할 수 있습니다.


이것이 필요한 이유

파이썬 오버로드 규칙

다른 해결책 대신 이렇게 하는 이유에 대한 설명은 다소 난해합니다.파이썬에는 오버로드 연산자와 비교 연산자에 대한 몇 가지 일반적인 규칙이 있습니다.

  1. 시 () LHS OP RHS.LHS.__op__(RHS), 그리고 만약 그것이 돌아온다면,NotImplemented.RHS.__rop__(LHS). : RHS의 하위 클래스입니다.LHS의 수업, 그 다음에 테스트.RHS.__rop__(LHS) 첫째. 비교법인의 경우,__eq__그리고.__ne__그들 자신의 "롭"입니다 (따라서 테스트 순서는 다음과 같습니다).__ne__이다.LHS.__ne__(RHS),그리고나서RHS.__ne__(LHS) , RHS의 하위 클래스입니다.LHS
  2. "교환된" 연산자의 개념을 제외하고, 연산자들 사이에 묵시적인 관계는 없습니다.를 들어도LHS.__eq__(RHS)gTrue는 .LHS.__ne__(RHS)sFalse(실제로 연산자는 부울 값을 반환할 필요도 없습니다. SQLAlchemy와 같은 ORM은 의도적으로 반환하지 않으므로 보다 표현력 있는 쿼리 구문을 허용합니다.)3 기준, 썬 3준,준__ne__다를 할 수 . 재정의할 수 있습니다.__ne____eq__.

이 기능이 오버로드되는 비교기에 적용되는 방식

연산자를 오버로드하면 두 가지 작업이 수행됩니다.

  1. 작업을 직접 수행하는 방법을 알고 있는 경우, 비교를 수행하는 방법에 대한 자신의 지식만을 사용하여 수행합니다(작업의 다른 측면에 대해 암묵적 또는 명시적으로 위임하지 마십시오. 수행 방법에 따라 오류 및/또는 무한 재귀가 발생할 수 있습니다.)
  2. 작업을 직접 수행하는 방법을 모르실 경우 항상 반환하십시오.NotImplemented인 파이썬 에 할 수 .

의 .not self.__eq__(other)

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

일 경우 함()__eq__ 된 .NotImplemented. 언제.self.__eq__(other)sNotImplemented 이다),다.False,그렇게A() != something_A_knows_nothing_aboutsFalse했어야 할 ,something_A_knows_nothing_about다의 을 알고 .A 않다면, 돌아왔어야 합니다, , .True(만약 어느 쪽도 상대방과 비교하는 방법을 모른다면, 그들은 서로 동등하지 않은 것으로 간주됩니다.) 만약A.__eq__됨()FalseNotImplemented에서)른 입니다"입니다.A의 의, True 로)A다)부터일 수 있습니다. 하지만 잘못된 것일 수도 있습니다.something_A_knows_nothing_aboutsomething_A_knows_nothing_about;A() != something_A_knows_nothing_abouts이 됩니다.True,그렇지만something_A_knows_nothing_about != A()할 수 있었다False 기타 값 즉 합니다.

의 .not self == other

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

더 미묘합니다.하여가 99%입니다하는 모든 하여 99__ne__입니다의 입니다.__eq__.그렇지만not self == other는 한 두 어깁니다.즉,합니다를 합니다.__ne__ 논리적으로 반대가 아닙니다.__eq__ 중 할 수 를 묻는 질문이 없기 한 번 하지 않습니다. 합니다.__ne__다른 피연산자가 못해도 말입니다가장 간단한 예는 돌아오는 이상한 수업입니다.False모든 비교에 있어서, 그러니까.A() == Incomparable()그리고.A() != Incomparable() 겸용hFalse한 구현을 합니다.A.__ne__ ()NotImplemented우),입니다).A() != Incomparable()그리고.Incomparable() != A()다우,A.__ne__sNotImplemented,그리고나서Incomparable.__ne__sFalse의 경우 , , Incomparable.__ne__sFalse직접).하지만 언제A.__ne__다로 됩니다.return not self == other,A() != Incomparable()sTrue 면)A.__eq__이 아닌 NotImplemented,그리고나서Incomparable.__eq__sFalse,그리고.A.__ne__다로 거죠True ), Incomparable() != A()sFalse.

여기에서 이것의 예시를 볼 수 있습니다.

를 , False__eq__그리고.__ne__좀 이상합니다.했듯이,__eq__그리고.__ne__True/False에는 SQLlchemy ORM 에는가.True/False(부울 문맥으로 평가하면 "진실"이지만, 절대 그런 문맥에서 평가해서는 안 됩니다.)

으로써 __ne__코드와 같은 종류의 클래스를 적절히 중단합니다.

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

가 합니다(SQLChemy 하면)를 있다고 하면).MyClassWithBadNE할 수 있습니다. SQL다가 어댑터를 할 수 있습니다.다.MyClassWithBadNE함)에게 함),합니다.filter, 다음 기간 동안:

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

결국 지나가게 될 것입니다.filteraFalse,self == other하고,not self == other를 truthy합니다.False, .filter는합니다와 예외를 합니다.False. 많은 사람들이 이렇게 주장할 것이라고 확신합니다.MyTable.fieldname 비교의 왼쪽에 일관되게 있어야 하며, 일반적인 경우에 이것을 시행할 프로그램적인 이유가 없다는 사실이 남아 있으며, 정확한 일반적인.__ne__어느 가 있을return not self == other하나의 배열로만 작동합니다.

구현__ne__

@ShadowRanger__ne__올바른 것입니다.

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

의 이기도 합니다.__ne__ 파이썬 문서에 명시된 바와 같이, 파이썬 3.4 이후:

으로,__ne__()__eq__()시킵니다.NotImplemented.

값을 하는 것은 합니다합니다.NotImplemented되지 않는 다 특수 .__ne__. 실제로 모든 특수 비교 방법과1 특수 숫자 방법2 Python 설명서에 명시된 대로 지원되지 않는 피연산자에 대한 값을 반환해야 합니다.

구현되지 않음

이 유형에는 단일 값이 있습니다.이 값을 가진 개체가 하나 있습니다. 제공 다를(를) .NotImplemented 경우 이 숫자 메서드와 리치 비교 메서드는 제공된 피연산자에 대한 연산을 구현하지 않는 경우 이 값을 반환해야 합니. 반사 (그러면 인터프리터는 연산자에 따라 반영된 연산이나 다른 폴백을 시도합니다.)그것의 진실한 가치는 진실입니다.

특별한 숫자 방법의 예는 Python 설명서에 나와 있습니다.

class MyIntegral(Integral):

    def __add__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(self, other)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(self, other)
        else:
            return NotImplemented

    def __radd__(self, other):
        if isinstance(other, MyIntegral):
            return do_my_adding_stuff(other, self)
        elif isinstance(other, OtherTypeIKnowAbout):
            return do_my_other_adding_stuff(other, self)
        elif isinstance(other, Integral):
            return int(other) + int(self)
        elif isinstance(other, Real):
            return float(other) + float(self)
        elif isinstance(other, Complex):
            return complex(other) + complex(self)
        else:
            return NotImplemented

1 특수 비교 방법:__lt__,__le__,__eq__,__ne__,__gt__그리고.__ge__.

2 특수 숫자 방법:__add__,__sub__,__mul__,__matmul__,__truediv__,__floordiv__,__mod__,__divmod__,__pow__,__lshift__,__rshift__,__and__,__xor__,__or__그리고 그들의__r*__d된__i*__제자리에 있는 대응물

__ne__#1

@Falmarri__ne__올바르지 않습니다.

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

은 입니다 입니다.__ne__하지 않으므로 NotImplemented 현)not self.__eq__(other) 평가합니다.True아니면False 하위 인 해서 말입니다.self.__eq__(other) 평가합니다.NotImplemented라는 표현 bool(NotImplemented) 평가합니다.True 부울 평가()입니다. 값의 부울 평가(Boolean 평가)NotImplemented비교 연산자 간의 보완 관계를 끊습니다.!=그리고.==:

class Correct:

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


class Incorrect:

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


x, y = Correct(), Correct()
assert (x != y) is not (x == y)

x, y = Incorrect(), Incorrect()
assert (x != y) is not (x == y)  # AssertionError

__ne__#2

Hall의 @Aaron Hall__ne__또한 틀립니다.

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

은 입니다 으로 의존한다는 입니다.__eq__것을 피연산자.__ne__하지 않으므로 NotImplemented 현)not self == other다에 합니다.__eq__ 및 합니다.True아니면False메서드를 바이패스하면 개체의 상태를 업데이트하는 등의 부작용이 발생할 수 있으므로 메서드를 바이패스하는 것은 올바르지 않습니다.

class Correct:

    def __init__(self):
        self.state = False

    def __ne__(self, other):
        self.state = True
        result = self.__eq__(other)
        if result is not NotImplemented:
            return not result
        return NotImplemented


class Incorrect:

    def __init__(self):
        self.state = False

    def __ne__(self, other):
        self.state = True
        return not self == other


x, y = Correct(), Correct()
assert x != y
assert x.state == y.state

x, y = Incorrect(), Incorrect()
assert x != y
assert x.state == y.state  # AssertionError

비교 작업의 이해

수학에서 집합 X에 대한 이항 관계 RX2 순서 쌍 (x, y)의 집합입니다.R의 문장 (x, y)는 'xy와 R 관련된 것이다'라고 읽으며 xRy로 표시됩니다.

집합 X에 대한 이항 관계 R의 속성:

  • X, xRx의 모든 x에 대하여 R반사적입니다.
  • xRx가 아닌 X의 모든 x에 대해 R무굴곡(엄격하다고도 함)입니다.
  • X의 모든 xy에 대해 대칭이고, xRy이면 yRx입니다.
  • X의 모든 xy에 대해 xRyyRx이면 x = y일R대칭이 됩니다.
  • X의 모든 x, y, z에 대하여 xRyyRz이면 xRz일R추이적입니다.
  • X, xRy 또는 yRx의 모든 xy에 대해 R코넥스(합계라고도 함)입니다.
  • RR이 반사적, 대칭적, 전이적일 때의 동등성 관계입니다.
    예를 들면 =.그러나 ≠는 대칭일 뿐입니다.
  • RR이 반사적이고 반대칭적이며 전이적일 때의 차수 관계입니다.
    예를 들어, ≤ 및 ≥.
  • R이 무굴곡, 반대칭 및 전이일 때 R은 엄격한 차수 관계입니다.
    예를 들어 < 및 >.하지만 ≠는 무반전적일 뿐입니다.

집합 X에 대한 두 이진 관계 RS에 대한 연산:

  • R반대X에 대한 이진 관계 R = {(y, x) | xRy}입니다.
  • R여집합X에 대한 이항 관계 ¬R = {(x, y) | not xRy}입니다.
  • RS결합X에 대한 이항 관계 RS = {(x, y) | xRy 또는 xSy}입니다.

항상 유효한 비교 관계 사이의 관계:

  • 2개의 상호보완적 관계: =와 ≠는 서로의 상호보완적인 관계;
  • 6개의 역관계: =는 자기 자신의 역, ≠는 자기 자신의 역, <와 >는 서로의 역, ≤와 ≥는 서로의 역,
  • 2조합관계: ≤은 <과 =의 결합이고, ≥은 >과 =의 결합입니다.

connex order 관계에만 유효한 비교 관계:

  • 4개의 보완관계: <와 ≥은 서로의 보완관계이며 >와 ≤는 서로의 보완관계입니다.

비교 연산자는 Python 에서를 합니다.==,!=,<,>,<=,그리고.>=비교 관계 =, ≠, <, >, ≤, ≥에 해당하는 위의 모든 수학적 속성과 관계가 유지되어야 합니다.

x operator y에서는 합니다라고 .__operator__ 중 다의 .

class X:

    def __operator__(self, other):
        # implementation

R반사적이기 때문에 xRx를 의미하기 때문에, 반사적인 비교 연산은x operator y(x == y,x <= y그리고.x >= y ) x.__operator__(y)(x.__eq__(y),x.__le__(y)그리고.x.__ge__(y)을 ) .True한다면x그리고.y즉식하다,하다x is y합니다.True. R비굴절적이므로 xRx가 아님을 의미하므로 비굴절 비교 연산x operator y(x != y,x < y그리고.x > y ) x.__operator__(y)(x.__ne__(y),x.__lt__(y)그리고.x.__gt__(y)을 ) .False한다면x그리고.y즉식하다,하다x is y합니다.True인 Python 합니다에 합니다.==법.__eq__하지만 놀랍게도 비교 연산자들에게는 고려되지 않았습니다.<=그리고.>= 특수 비교 법.__le__그리고.__ge__인 합니다에 하고 있습니다.!=법.__ne__하지만 놀랍게도 비교 연산자들에게는 고려되지 않았습니다.<그리고.> 특수 비교 법.__lt__그리고.__gt__ 합니다 합니다.TypeError을 반환합니다합니다).NotImplemented), 파이썬 문서에서 설명한 바와 같이:

()==그리고.!=으로 합니다는 개체의 ID를 기반으로 합니다.따라서 동일한 동일성을 가진 인스턴스의 동일성 비교는 동일성으로 귀결되고, 다른 동일성을 가진 인스턴스의 동일성 비교는 불평등으로 귀결됩니다.의 동기는 이어야 한다는 입니다)입니다.x is yx == y).

()<,>,<=,그리고.>=하면 는가 합니다. 시도가 발생합니다.TypeError. 이러한 기본 동작의 동기는 동등성에 대한 유사한 불변성이 없기 때문입니다.[이것은 와 같이 부정확하고, 와 같이 반사적이며, 와 같이 반사적입니다.]

.object에서는 Python 문서에서 설명한 대로 모든 하위 클래스에서 상속되는 특수 비교 메서드의 기본 구현을 제공합니다.

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<yx.__lt__(y),x<=yx.__le__(y),x==yx.__eq__(y),x!=yx.__ne__(y),x>yx.__gt__(y),그리고.x>=yx.__ge__(y).

를 할 수 .NotImplemented지정된 인수 쌍에 대한 연산을 구현하지 않는 경우.

[…]

에는 스왑된 되는 경우 ( 됩니다).__lt__()그리고.__gt__()입니다.__le__()그리고.__ge__()이고입니다__eq__()그리고.__ne__()그들 자신의 반영입니다.피연산자가 다른 유형이고 오른쪽 피연산자 유형이 왼쪽 피연산자 유형의 직접 또는 간접 하위 클래스인 경우 오른쪽 피연산자의 반사 방식이 우선 순위를 가지며, 그렇지 않으면 왼쪽 피연산자의 방식이 우선 순위를 갖습니다.가상 서브클래싱은 고려되지 않습니다.

R = (R)이므로 비교 xRy역비교 yRx(Python 문서에서 비공식적으로 'reflected'로 명명됨)와 같습니다.따라서 비교 연산의 결과를 계산하는 두 가지 방법이 있습니다.x operator y 중 를 호출하는 중 : x.__operator__(y)아니면y.__operatorT__(x)과 같은 파이썬은 다음과 같은 컴퓨팅 전략을 사용합니다.

  1. 은라고 부릅니다.x.__operator__(y) 피연산자의한라고 .y.__operatorT__(x)(조상의 역특수비교법을 무시하기 위해 클래스를 allowing합니다.)
  2. 피연산자가.x그리고.y되지 않습니다 값음으로 표시).NotImplemented), 역 특수 비교법을 1차 폴백이라고 합니다.
  3. 피연산자가.x그리고.y되지 않습니다 값음으로 표시).NotImplemented), 합니다.TypeError합니다.==그리고.!=와 y을 하고 아이덴티티합니다를비합니다.x그리고.y번째 폴백(의 반사율 특성을 lever링함)으로==.!=).
  4. 결과를 반환합니다.

C python에서 이것은 C 코드로 구현되는데, 이것은 Python 코드로 번역될 수 있습니다 (이름들과 함께).eq위해서==,ne위해서!=,lt위해서<,gt위해서>,le위해서<=그리고.ge위해서>=):

def 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 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 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__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def 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__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def 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__}' "
            f"and '{type(right).__name__}'"
        )
    return result
def 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__}' "
            f"and '{type(right).__name__}'"
        )
    return result

R = ¬(¬R)이므로 비교 xRy는 보 비교 ¬(x ¬Ry)와 같습니다.≠ 는 =의 보완이므로 특별한 방법입니다.__ne__ 방법됩니다의 됩니다.__eq__기본적으로 지원되는 피연산자의 경우, 다른 특별한 비교 방법은 기본적으로 독립적으로 구현됩니다(≤는 <과 =의 결합이고, ≥는 >과 =의 결합이라는 사실은 놀랍게도 고려되지 않습니다). 즉, 현재 특별한 방법은__le__그리고.__ge__를 구현해야 합니다). Python 설명서에서 설명한 바와 같이:

으로,__ne__()__eq__()시킵니다.NotImplemented에는 다른 를 들어 . 의 입니다. 예를 들어, 다음과 같은 진실이 있습니다.(x<y or x==y)는 .x<=y.

CPython에서 이것은 C 코드로 구현되며, Python 코드로 번역할 수 있습니다.

def __eq__(self, other):
    return self is other or 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

기본적으로 다음과 같습니다.

  • x operator y합니다를 .TypeError합니다.==그리고.!=이 합니다 합니다.True그리고.Falsex그리고.y이 동일한다를비합니다.False그리고.True그렇지 않으면,
  • x.__operator__(y)합니다를(를) 합니다.NotImplemented__eq__그리고.__ne__이 합니다 합니다.True그리고.Falsex그리고.y한 값과 값이 다를과합니다.NotImplemented그렇지않으면.

언급URL : https://stackoverflow.com/questions/4352244/should-ne-be-implemented-as-the-negation-of-eq

반응형