답변:
예, 완벽합니다. 실제로 문서 에서는 다음을 정의 __ne__
할 때 정의하도록 촉구합니다 __eq__
.
비교 연산자 간에는 암시 적 관계가 없습니다. 의 진실은
x==y
그것이x!=y
거짓 임을 의미하지 않습니다 . 따라서 정의 할 때 연산자가 예상대로 작동하도록__eq__()
정의__ne__()
해야합니다.
많은 경우 (예 :이 경우)의 결과를 부정하는 것처럼 간단 __eq__
하지만 항상 그런 것은 아닙니다.
NotImplemented
위임에 큐와 같은 하나 개의 측면에서 수익을 __ne__
다른 측면에서, not self == other
(피연산자의이 가정되는 __eq__
암시 적으로 위임 다른 피연산자를 비교하는 방법을 알고하지 않습니다) __eq__
다음 반전, 다른 측면에서. SQLAlchemy ORM의 필드와 같은 이상한 유형의 경우 이로 인해 문제가 발생 합니다.
__ne__
자동으로 위임 __eq__
되며이 답변의 인용문은 더 이상 문서에 없습니다. 결론적으로 구현 __eq__
하고 __ne__
위임 하는 것만이 완벽하게 비단뱀 적 입니다.
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
!
에 대한 기본 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 (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__
?더 높은 수준 ( PyObject_RichCompare )이 덜 효율적 이기 때문에 __ne__
C 수준에서 Python 3의 기본 구현 세부 정보를 사용 __eq__
하므로 처리해야합니다.==
NotImplemented
.
경우 __eq__
올바르게 구현, 다음의 부정은 ==
또한 올 - 그것은 우리가 우리의 낮은 수준의 구현 세부 사항을 피할 수 있습니다 __ne__
.
사용은 ==
우리의 낮은 수준의 논리를 유지하기 위해 우리를 허용 한 곳, 그리고 피 주소 NotImplemented
에 __ne__
.
==
반환 될 수 있다고 잘못 가정 할 수 있습니다 NotImplemented
.
실제로 __eq__
ID를 확인하는 의 기본 구현과 동일한 논리를 사용합니다 (아래의 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__
절대 반환 할 수 없기 때문에 올바르지 않으므로 우선 순위가 있는 메서드는 우선 순위가없는 메서드로 대체 될 수 없습니다 .NotImplemented
not NotImplemented
False
__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__
직접 또는 통해 위임하는 방법을 포함하여 모든 관련 방법이 제거 되었으며 가장 일반적인 방법이었습니다.==
==
우리의 끊임없는 비평가는 무엇보다 대칭을 중요시 하는 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)
Python 2 호환 코드의 경우를 사용 ==
하여 __ne__
. 더 많은 것 :
Python 3에서만 C 수준에서 저수준 부정을 사용합니다. 훨씬 더 간단하고 성능이 뛰어납니다 (프로그래머가 올바른지 판단 할 책임이 있습니다 ).
다시 말하지만, 고수준 Python에서 저수준 논리를 작성 하지 마십시오 .
a1 != c2
반환 한 이유입니다 False
.--- 실행되지 a1.__ne__
않았지만 mixin의 방법 c2.__ne__
을 부정했습니다 . 이후 truthy이다, 입니다 . __eq__
NotImplemented
not NotImplemented
False
not (self == other)
었지만 아무도 빠르지 않다고 주장하지 않습니다 (음, 어쨌든 Py2의 다른 옵션보다 빠름). 문제는 어떤 경우에는 잘못되었다는 것 입니다. 파이썬 자체가 예전에는 그렇게 not (self == other)
했지만, 임의의 서브 클래스가있을 때 잘못 되었기 때문에 변경되었습니다 . 오답에 대한 가장 빠른 것은 여전히 잘못되었습니다 .
__ne__
델리게이트 의 동작 __eq__
(필요한 경우 양쪽 모두)이지만, 둘 다 "포기" 하더라도 다른 쪽 의 동작 으로 절대 돌아 가지 않는다는 것 입니다. 올바른 델리게이트가 자신 의 에게 반환 되면 다른 쪽을 반전하는 대신 다른 쪽의으로 돌아가도록 폴백합니다 (다른 쪽이 에 위임하도록 명시 적으로 옵트 인하지 않았을 수 있으므로 그렇게 하지 않아야합니다. 그 결정을 내릴 것입니다). __ne__
__eq__
__ne__
__eq__
NotImplemented
__ne__
__eq__
__eq__
__eq__
도 __ne__
반환하거나 True
또는 False
아니라 ( "truthy"될 일) 프록시 객체입니다. 잘못 구현 __ne__
하면 비교에 순서가 중요합니다 (한 번의 순서로만 프록시를 얻음).
__ne__
완전히 생략 할 수 있기 때문에이 모든 것이 중요하지 않습니다 . 지금부터 1 년 후에 Py2는 죽을 것이고 우리는 이것을 무시합니다. :-)
기록을 위해 정규적으로 정확하고 교차하는 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__
합니다 NotImplemented
( not self.__eq__(other)
사실이기 때문에 추가로 잘못 될 수 NotImplemented
있으므로 __eq__
비교를 수행하는 방법을 몰랐을 때를 __ne__
반환합니다 False
. 이는 실제로 유일한 경우 두 객체가 동일하다는 것을 의미합니다. 요청 된 개체는 전혀 모름, 같지 않음의 기본값을 의미 함)당신이 경우 __eq__
사용하지 않는 NotImplemented
반환, (의미 오버 헤드)이 작품은 사용 않는 경우 NotImplemented
제대로 때때로,이 핸들을. 그리고 Python 버전 검사는 클래스가 import
Python 3에서 -ed 인 경우 __ne__
정의되지 않은 상태로 유지되어 Python의 기본적이고 효율적인 폴백 __ne__
구현 (위의 C 버전) 이 대신 할 수 있음을 의미합니다.
다른 솔루션 대신이 작업을 수행하는 이유에 대한 설명은 다소 난해합니다. Python에는 연산자 오버로딩과 특히 비교 연산자에 대한 몇 가지 일반적인 규칙이 있습니다.
LHS OP RHS
, 시도LHS.__op__(RHS)
,이 값이 반환 NotImplemented
되면 try RHS.__rop__(LHS)
. 예외 : RHS
이 클래스의 하위 클래스 인 경우 먼저LHS
테스트하십시오 . 비교 연산자의 경우, 과 에 대한 테스트 순서가 있으므로 자신의 'ROP'의이다 ( 이다 , 다음 경우, 반전 의 서브 클래스RHS.__rop__(LHS)
__eq__
__ne__
__ne__
LHS.__ne__(RHS)
RHS.__ne__(LHS)
RHS
LHS
의 클래스)LHS.__eq__(RHS)
반환 True
은 LHS.__ne__(RHS)
반환을 의미하지 않습니다 False
(사실 연산자는 부울 값을 반환 할 필요도 없습니다. SQLAlchemy와 같은 ORM은 의도적으로 그렇지 않아보다 표현적인 쿼리 구문을 허용합니다). Python 3부터 기본 __ne__
구현은 이러한 방식으로 작동하지만 계약이 아닙니다. __ne__
.NET의 엄격한 반대가 아닌 방식으로 재정의 할 수 있습니다 __eq__
.따라서 연산자를 오버로드하면 두 가지 작업이 있습니다.
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_about
최대 종료 할 수True
하지만,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__
반환 NotImplemented
한 Incomparable.__ne__
다음를 반환 False
하고 후자의 경우 직접 Incomparable.__ne__
반환하기 때문입니다 False
). 그러나 경우 A.__ne__
로 구현됩니다 return not self == other
, A() != Incomparable()
반환 True
(때문에 A.__eq__
반환하지 NotImplemented
후 Incomparable.__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가 MyClassWithBadNE
SQL 문자열에 삽입하는 방법을 전혀 알고 있다고 가정 합니다.이 작업 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__
메소드 구현은 올바른 것입니다 ( __ne__
파이썬 3.4 이후 메소드 의 기본 구현입니다 ).
def __ne__(self, other):
result = self.__eq__(other)
if result is not NotImplemented:
return not result
return NotImplemented
왜? 중요한 수학적 속성 인 연산자 의 대칭 을 유지하기 때문 !=
입니다. 이 연산자는 바이너리 이므로 그 결과는 하나가 아닌 두 피연산자 의 동적 유형에 따라 달라집니다 . 이것은 다중 디스패치를 허용 하는 프로그래밍 언어에 대한 이중 디스패치 를 통해 구현됩니다. (예 : Julia )에 . 단일 디스패치 만 허용하는 Python에서는 값을 반환하여 숫자 메서드 와 풍부한 비교 메서드 에 대해 이중 디스패치를 시뮬레이션 합니다.NotImplemented
다른 피연산자의 유형을 지원하지 않는 구현 메소드 . 그러면 인터프리터는 다른 피연산자의 반영된 방법을 시도합니다.
Aaron Hall의 방법 구현 not self == other
은 운영자 __ne__
의 대칭을 제거하기 때문에 올바르지 않습니다 !=
. 사실, 결코 돌아올 수 없습니다NotImplemented
( not NotImplemented
is False
)를__ne__
우선 순위가 높은 __ne__
메서드는 우선 순위가 낮은 메서드로 폴백 할 수 없습니다 . not self == other
이전에는 __ne__
메서드 의 기본 Python 3 구현 이었지만 ShadowRanger가 발견 한대로 2015 년 1 월 Python 3.4에서 수정 된 버그였습니다 ( 문제 # 21408 참조) . ).
파이썬 언어 참조 파이썬에 대한 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<y
callsx.__lt__(y)
,x<=y
callsx.__le__(y)
,x==y
callsx.__eq__(y)
,x!=y
callsx.__ne__(y)
,x>y
callsx.__gt__(y)
및x>=y
callsx.__ge__(y)
.풍부한 비교 메서드는
NotImplemented
주어진 인수 쌍에 대한 연산을 구현하지 않으면 싱글 톤을 반환 할 수 있습니다 .이러한 메서드의 인수가 교체 된 버전은 없습니다 (왼쪽 인수가 작업을 지원하지 않지만 오른쪽 인수가 지원할 때 사용됨). 오히려,
__lt__()
및__gt__()
서로의 반영이다,__le__()
그리고__ge__()
서로의 반사하고,__eq__()
그리고__ne__()
자신의 반영이다. 피연산자가 다른 유형이고 오른쪽 피연산자의 유형이 왼쪽 피연산자 유형의 직접 또는 간접 서브 클래스 인 경우 오른쪽 피연산자의 반영된 메소드가 우선 순위를 가지며 그렇지 않으면 왼쪽 피연산자의 메소드가 우선 순위를 갖습니다. 가상 서브 클래 싱은 고려되지 않습니다.
이것을 파이썬 코드로 번역하면 ( operator_eq
for ==
, operator_ne
for !=
, operator_lt
for <
, operator_gt
for >
, operator_le
for<=
및 operator_ge
for 사용 >=
).
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__
할 때 역 이 (as) 이기 때문에 항상 메서드 의 역을 반환하지는 않습니다.__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__"
!=
전화 A.__ne__
.A.__ne__
전화 A.__eq__
.A.__eq__
를 반환합니다 NotImplemented
.!=
전화 B.__ne__
.B.__ne__
를 반환합니다 "B.__ne__"
.때이 쇼 A.__eq__
방법 반환 NotImplemented
의 A.__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
!=
전화 A.__ne__
.A.__ne__
전화 A.__eq__
.A.__eq__
를 반환합니다 True
.!=
반환 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
!=
전화 A.__ne__
.A.__ne__
전화 ==
.==
전화 A.__eq__
.A.__eq__
를 반환합니다 NotImplemented
.==
전화 B.__eq__
.B.__eq__
를 반환합니다 NotImplemented
.==
반환 A() is B()
, 즉 False
.A.__ne__
반환 not False
, 즉 True
.__ne__
반환 된 메서드 의 기본 구현은 "B.__ne__"
아닙니다 True
.
이제 구현으로 A.__ne__
메서드 의 기본 구현을 재정의하고 메서드가 다음 not self == other
과 A.__eq__
다른 값을 반환 할 때 어떤 일이 발생하는지 살펴 보겠습니다 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
!=
전화 A.__ne__
.A.__ne__
전화 ==
.==
전화 A.__eq__
.A.__eq__
를 반환합니다 True
.A.__ne__
반환 not True
, 즉 False
.__ne__
메서드 의 기본 구현 도 반환되었습니다.False
이 경우 됩니다.
이 구현 __ne__
은 __eq__
메서드가를 반환 할 때 메서드 의 기본 구현 동작을 복제하지 못하므로 NotImplemented
잘못된 것입니다.
__ne__
은 __eq__
메서드가 NotImplemented를 반환 할 때 메서드 의 기본 구현 동작을 복제하지 못하므로 잘못된 것입니다." - A
무조건 평등을 정의합니다. 따라서 A() == B()
. 따라서 A() != B()
거짓이어야한다 , 그것은 이다 . 주어진 예는 병리 적입니다 (즉 __ne__
, 문자열을 반환 __eq__
해서는 안되며, 의존해서는 안됩니다. __ne__
대신 Python 3의 기본 기대치 인에 __ne__
의존해야합니다 __eq__
). 내 마음을 바꿀 수있을 때까지 나는 여전히이 대답에 -1입니다.
NotImplemented
.이 인수의 주어진 쌍에 대한 작업을 구현하고 있지 않는 경우 관례 적으로, False
그리고 True
성공적인 비교를 위해 반환됩니다. 그러나, 이러한 방법은 어떤 값을 반환 할 수 있습니다 따라서 비교 연산자가 부울 컨텍스트 (예 : if 문의 조건에서)에서 사용되는 경우 Python은 bool()
값을 호출 하여 결과가 참인지 거짓인지를 결정합니다. "
B
그 반환에 대한 모든 검사에 truthy 문자열 __ne__
, 그리고 A
그 반환 True
에 대한 모든 검사에 __eq__
. 이것은 병리학 적 모순입니다. 이러한 모순에서 예외를 제기하는 것이 가장 좋습니다. 지식없이 B
, A
존중 의무가 B
의 구현 __ne__
대칭의 목적을. 예제의 그 시점에서 A
구현 __ne__
은 나에게 무관합니다. 당신의 요점을 만들기 위해 실용적이고 비 병리적인 사례를 찾으십시오. 나는 당신을 해결하기 위해 내 답변을 업데이트했습니다.
__ne__
일반적인 사용 사례에서 작업을 구현 하는 것이 올바른 방법이 아닙니다. Boeing 737 MAX 항공기는 추락 전에 50 만 대를 비행했습니다…
모두의 경우 __eq__
, __ne__
, __lt__
, __ge__
, __le__
, 및 __gt__
클래스의 메이크업 감각, 그럼 그냥 구현 __cmp__
대신. 그렇지 않으면 Daniel DiPaolo가 말한 비트 때문에 (내가 그것을 찾는 대신 테스트하는 동안;))
__cmp__()
특별한 방법은 더 이상 부자 비교 연산자를 사용하여 익숙해한다고, 그래서 파이썬 3.x를 지원하지 않습니다.
__ne__
하는__eq__
것만 권장 합니다.