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__
기능을 모두 호출하는 것 같습니다 .
공식 의사 결정 트리를 찾고 있습니다.
답변
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 ?
답변
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)
은True
iffa
이고b
동일한 객체 인을 호출 합니다.
특수 메서드가를 반환 NotImplemented
하면 Python은 메서드가 존재하지 않는 것처럼 작동합니다.
마지막 단계는주의 깊게 참고하십시오. 두 경우 모두 a
또는 b
과부하 ==
가 아니면 a == b
과 동일합니다 a is b
.
답변
이 질문에 대한 Python 3에 대한 업데이트 된 답변을 작성하고 있습니다.
__eq__
파이썬에서는 어떻게 처리되며 어떤 순서로 처리됩니까?a == b
그것은 일반적으로 이해되지만, 항상 경우, 그 a == b
발동 할 a.__eq__(b)
, 또는 type(a).__eq__(a, b)
.
명시 적으로 평가 순서는 다음과 같습니다.
- 경우
b
의 유형의 엄격한 서브 (아니라 동일한 유형) 인a
S 형 ‘및를 가지고__eq__
, 호출 및 상기 비교가 실행되는 경우에 값을 반환 - 또, 경우
a
가__eq__
, 그것을 호출하고 비교가 구현되는 경우를 반환, - 그렇지 않으면 우리가 b를 호출하지 않았고
__eq__
그것을 가지고 있는지 확인한 다음 비교가 구현되면 호출하고 반환합니다. - 그렇지 않으면 마지막으로 동일성에 대한 비교를 수행합니다
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;
결론
비교에서 우리는 먼저 비교의 하위 클래스 구현을 존중합니다.
그런 다음 첫 번째 객체의 구현과 비교를 시도하고 호출되지 않은 경우 두 번째 객체와 비교를 시도합니다.
마지막으로 동일성 비교를 위해 동일성 테스트를 사용합니다.