__eq__는 파이썬에서 어떻게 그리고 어떤 순서로 처리됩니까?


98

Python은 비교 연산자의 왼쪽 / 오른쪽 버전을 제공하지 않기 때문에 호출 할 함수를 어떻게 결정합니까?

class A(object):
    def __eq__(self, other):
        print "A __eq__ called"
        return self.value == other
class B(object):
    def __eq__(self, other):
        print "B __eq__ called"
        return self.value == other

>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False

이것은 두 __eq__기능을 모두 호출하는 것 같습니다 .

공식 의사 결정 트리를 찾고 있습니다.

답변:


119

a == b표현은 호출 A.__eq__이 존재하기 때문에. 코드에는 self.value == other. int는 자신을 B와 비교하는 방법을 모르기 때문에 Python B.__eq__은 자신을 int와 비교하는 방법을 알고 있는지 확인하기 위해 호출 을 시도합니다 .

비교되는 값을 표시하도록 코드를 수정하는 경우 :

class A(object):
    def __eq__(self, other):
        print("A __eq__ called: %r == %r ?" % (self, other))
        return self.value == other
class B(object):
    def __eq__(self, other):
        print("B __eq__ called: %r == %r ?" % (self, other))
        return self.value == other

a = A()
a.value = 3
b = B()
b.value = 4
a == b

다음과 같이 인쇄됩니다.

A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ?
B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?

69

Python2.x가를 발견 a == b하면 다음을 시도합니다.

  • 경우 type(b)새로운 스타일의 클래스이며, type(b)의 서브 클래스 type(a), 그리고 type(b)무시했다 __eq__, 결과입니다 b.__eq__(a).
  • 경우 type(a)오버라이드 (override)가 __eq__(이라고 type(a).__eq__하지 않습니다 object.__eq__), 결과입니다 a.__eq__(b).
  • type(b)재정의 된 경우 __eq__결과는 b.__eq__(a)입니다.
  • 위의 경우에 해당하지 않는 경우 Python은 __cmp__. 존재하는 경우 객체는를 반환하면 동일합니다 zero.
  • 최종 폴백으로 Python object.__eq__(a, b)Trueiff a이고 b동일한 객체 인을 호출 합니다.

특수 메서드가를 반환 NotImplemented하면 Python은 메서드가 존재하지 않는 것처럼 작동합니다.

마지막 단계는주의 깊게 참고하십시오. 두 경우 모두 a또는 b과부하 ==가 아니면 a == b과 동일합니다 a is b.


에서 https://eev.ee/blog/2012/03/24/python-faq-equality/


1
python 3 문서가 잘못된 것 같습니다. 자세한 설명 은 bugs.python.org/issue4395 및 패치를 참조하세요 . TLDR : 하위 클래스가 rhs에 있더라도 여전히 먼저 비교됩니다.
최대

안녕하세요 kev, 멋진 게시물입니다. 첫 번째 글 머리 기호가 어디에 문서화되어 있으며 왜 그렇게 설계되었는지 설명해 주시겠습니까?
WIM

1
네, 이것은 파이썬 2에 대해 어디에 문서화되어 있습니까? PEP입니까?
Mr_and_Mrs_D

이 답변과 수반되는 의견을 바탕으로 이전보다 더 혼란스러워졌습니다.
Sajuuk

그리고 btw, __eq__ ==가 재정의하기에 충분하지 않은 일부 유형의 인스턴스에 바인딩 된 메서드 를 정의 합니까?
Sajuuk

5

이 질문에 대한 Python 3에 대한 업데이트 된 답변을 작성하고 있습니다.

__eq__파이썬에서는 어떻게 처리되며 어떤 순서로 처리됩니까?

a == b

그것은 일반적으로 이해되지만, 항상 경우, 그 a == b발동 할 a.__eq__(b), 또는 type(a).__eq__(a, b).

명시 적으로 평가 순서는 다음과 같습니다.

  1. 경우 b의 유형의 엄격한 서브 (아니라 동일한 유형) 인 aS 형 '및를 가지고 __eq__, 호출 및 상기 비교가 실행되는 경우에 값을 반환
  2. 또, 경우 a__eq__, 그것을 호출하고 비교가 구현되는 경우를 반환,
  3. 그렇지 않으면 우리가 b를 호출하지 않았고 __eq__그것을 가지고 있는지 확인한 다음 비교가 구현되면 호출하고 반환합니다.
  4. 그렇지 않으면 마지막으로 동일성에 대한 비교를 수행합니다 is.

메서드가을 반환하면 비교가 구현되지 않았는지 알고 NotImplemented있습니다.

(Python 2에는 __cmp__검색된 메서드가 있었지만 Python 3에서는 더 이상 사용되지 않고 제거되었습니다.)

B 하위 클래스 A를 허용하여 첫 번째 검사의 동작을 테스트 해 보겠습니다. 그러면이 카운트에서 허용 된 답변이 잘못되었음을 알 수 있습니다.

class A:
    value = 3
    def __eq__(self, other):
        print('A __eq__ called')
        return self.value == other.value

class B(A):
    value = 4
    def __eq__(self, other):
        print('B __eq__ called')
        return self.value == other.value

a, b = A(), B()
a == b

B __eq__ called반환하기 전에 인쇄합니다 False.

이 전체 알고리즘을 어떻게 알 수 있습니까?

여기에 다른 대답은 내가 정보를 업데이트하는거야, 그래서 불완전하고 오래된 것 그리고 자신이를 찾아 볼 수있는 방법을 방법을 보여줍니다.

이것은 C 레벨에서 처리됩니다.

여기서 우리는 두 개의 다른 코드 비트를 볼 필요가 있습니다. 즉, __eq__class의 객체에 대한 기본값과 기본값 을 사용하는지 아니면 사용자 정의를 사용하는지에 관계없이 메서드 object를 조회하고 호출하는 코드입니다 .__eq____eq__

기본 __eq__

찾고 __eq__관련 C의 API 문서를 보여줍니다 우리를 그 __eq__에 의해 처리됩니다 tp_richcompare에있는 - "object"유형 정의에가 cpython/Objects/typeobject.c에 정의 object_richcompare를위한 case Py_EQ:.

    case Py_EQ:
        /* Return NotImplemented instead of False, so if two
           objects are compared, both get a chance at the
           comparison.  See issue #1393. */
        res = (self == other) ? Py_True : Py_NotImplemented;
        Py_INCREF(res);
        break;

그래서 여기, 경우 self == other잠시 후 True, 다른 우리는 반환 NotImplemented객체를. 이것은 자체 __eq__메서드를 구현하지 않는 개체의 모든 하위 클래스에 대한 기본 동작입니다 .

어떻게 __eq__불려

그런 다음 C API 문서 인 PyObject_RichCompare 함수를 찾습니다 do_richcompare.

그러면 C 정의를 tp_richcompare위해 생성 된 함수 "object"가에 의해 호출되는 do_richcompare것을 볼 수 있으므로 좀 더 자세히 살펴 보겠습니다.

이 함수의 첫 번째 검사는 비교되는 개체의 조건에 대한 것입니다.

  • 있다 없다 같은 종류지만,
  • 두 번째 유형은 첫 번째 유형의 하위 클래스이고
  • 두 번째 유형에는 __eq__메서드가 있습니다.

그런 다음 인수를 바꾸어 다른 메서드를 호출하여 구현 된 경우 값을 반환합니다. 해당 방법이 구현되지 않은 경우 계속 진행합니다.

    if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
        PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
        (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        checked_reverse_op = 1;
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);

다음으로 __eq__첫 번째 유형에서 메서드를 찾아서 호출 할 수 있는지 확인 합니다. 결과가 NotImplemented가 아닌 한, 즉 구현 된 경우 반환합니다.

    if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
        res = (*f)(v, w, op);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);

그렇지 않으면 다른 유형의 방법을 시도하지 않고 거기에 있으면 시도하고 비교가 구현되면 반환합니다.

    if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }

마지막으로, 두 유형 모두에 대해 구현되지 않은 경우 대체를 얻습니다.

폴백은 객체의 ID, 즉 메모리의 동일한 위치에있는 동일한 객체인지 여부를 확인합니다. 이는 다음과 같은 검사입니다 self is other.

    /* If neither object implements it, provide a sensible default
       for == and !=, but raise an exception for ordering. */
    switch (op) {
    case Py_EQ:
        res = (v == w) ? Py_True : Py_False;
        break;

결론

비교에서 우리는 먼저 비교의 하위 클래스 구현을 존중합니다.

그런 다음 첫 번째 객체의 구현과 비교를 시도하고 호출되지 않은 경우 두 번째 객체와 비교를 시도합니다.

마지막으로 동일성 비교를 위해 동일성 테스트를 사용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.