2010-02-17 10 views
35

파이썬 문서에는 x==yx.__eq__(y)으로 명확하게 표시되어 있습니다. 그러나 많은 경우에 그 반대가 사실 인 것으로 보인다. 이 문제가 언제 또는 왜 발생했는지, 그리고 내 개체의 __cmp__ 또는 __eq__ 메서드가 호출되는지 여부를 확인하려면 어떻게해야합니까?이유/파이썬에서`x == y`가`y .__ eq __ (x)`를 호출 할 때?

편집 : 그냥 내가 __eq____cmp__에 preferecne에서 호출되는 것을 알고 있지만, y.__eq__(x)는 후자의 문서 상태가 일어날 것입니다 x.__eq__(y)에 우선이라고하는 이유는 명확하지 않다 명확합니다.

>>> class TestCmp(object): 
...  def __cmp__(self, other): 
...   print "__cmp__ got called" 
...   return 0 
... 
>>> class TestEq(object): 
...  def __eq__(self, other): 
...   print "__eq__ got called" 
...   return True 
... 
>>> tc = TestCmp() 
>>> te = TestEq() 
>>> 
>>> 1 == tc 
__cmp__ got called 
True 
>>> tc == 1 
__cmp__ got called 
True 
>>> 
>>> 1 == te 
__eq__ got called 
True 
>>> te == 1 
__eq__ got called 
True 
>>> 
>>> class TestStrCmp(str): 
...  def __new__(cls, value): 
...   return str.__new__(cls, value) 
...  
...  def __cmp__(self, other): 
...   print "__cmp__ got called" 
...   return 0 
... 
>>> class TestStrEq(str): 
...  def __new__(cls, value): 
...   return str.__new__(cls, value) 
...  
...  def __eq__(self, other): 
...   print "__eq__ got called" 
...   return True 
... 
>>> tsc = TestStrCmp("a") 
>>> tse = TestStrEq("a") 
>>> 
>>> "b" == tsc 
False 
>>> tsc == "b" 
False 
>>> 
>>> "b" == tse 
__eq__ got called 
True 
>>> tse == "b" 
__eq__ got called 
True 

편집 :

  1. 리치 비교 __cmp__
  2. __eq__는 그것이 __op__ (그리고 __lt__, __ge__ 등을위한 유사 __rop__에 자신의입니다 오버라이드 (override) : 마크 디킨슨의 대답에서하는 것은 그 나타납니다 언급)
  3. 왼쪽 개체가 기본 또는 새 스타일 클래스이고 오른쪽이 하위 클래스 인 경우 올바른 개체의왼쪽 개체의 __op__

이는 TestStrCmp 예제의 동작을 설명하기 전에이 시도됩니다. TestStrCmpstr의 하위 클래스이지만 __eq__을 구현하지 않으므로 str__eq__이 두 경우 모두에서 우선합니다 (즉, tsc == "b"b.__eq__(tsc)을 규칙 1로 인해 __rop__으로 호출 함).

TestStrEq의 예제에서 TestStrEqstr의 하위 클래스이므로 기본 설정에서 호출되므로 tse.__eq__이 두 인스턴스에서 호출됩니다.

TestEq 예에서

, TestEq__eq__ 구현하고 int 그렇게 __eq__은 (규칙 1) 두 번 호출됩니다하지 않습니다.

하지만 여전히 첫 번째 예가 TestCmp으로 이해되지 않습니다. tcint의 하위 클래스가 아니므로 AFAICT 1.__cmp__(tc)을 호출해야하지만 그렇게하지 않아야합니다.

답변

29

의 따라서 호출을 정의되지 않은, 풍부한 비교 방법이며 다음과 같은 경우 오른쪽 피연산자는 왼쪽 피연산자의 클래스의 하위 클래스의 인스턴스이고, 오른쪽 피연산자의 특별한 메서드가 먼저 호출됩니다. ,

http://docs.python.org/reference/datamodel.html#coercion-rules

특히 다음의 두 단락 : x.__op__(y) 시도되는 최초의 객체 xy를 들어

은 설명서를 참조하십시오. 이 구현되지 않았거나 NotImplemented을 반환하는 경우 y.__rop__(x)은 시도한 입니다. 이것도 구현되어 있지 않은 경우 또는 NotImplemented을 반환하면 TypeError 예외가 발생합니다. 이전 항목에

예외 :하지만 에게 다음과 같은 예외를 참조 왼쪽 피연산자가 내장 된 유형 또는 새로운 스타일의 클래스 오른쪽 피연산자 의 인스턴스의 경우는 것은의 인스턴스 입니다 그 종류 또는 적절한 서브 클래스 및 오른쪽 피연산자의 __rop__() 방법 왼쪽 피연산자의 __op__() 전에 방법을 시도하고, 상기베이스의 __rop__() 방법을 대체.

+0

@Daniel Pryden : 서식 수정에 감사드립니다! 나는 다음에 blockquote를 기억하려고 노력할 것이다. –

+0

좋은 생각이지만, (확실하지는 않지만) 모든'__rop__' 메쏘드는 더 이상 사용되지 않을 것이라고 생각했습니다. 또한 나는 그들 중 어느 것도 사용하지 않고있다. – Singletoned

+4

'__rop__' 메서드를 사용하지 않는다고 동의합니다. 비교 방법은이 점에서 특별합니다 :'__eq__'는 그것의 역방향이므로'__op__'과'__rop__' 모두'__eq__'를 읽으십시오. (비슷하게,'__ne__'은 그것의 역방향이고,'__le__'은'__ge__와 반대입니다. ') 다른 사람들은 (정확하게, IMO) 전에 문서가 여기에서 어떤 작업을 사용할 수 있다고 언급했습니다. '__rop__' 메소드가 사용되지 않는 것은 거의 확실합니다! –

1

Language Reference에 기록되어 있지 않습니까? 잠깐 살펴 보니 은 무시됩니다. __eq__, __lt__ 등이 정의되어 있습니다. __eq__이 부모 클래스에 정의 된 경우를 포함하는 것을 이해하고 있습니다. 서브 클래스의 __cmp__이 무시되도록 str.__eq__이 이미 정의되어 있습니다. object.__eq__ 등은 정의되어 있지 않으므로 하위 클래스의 __cmp__이 적용됩니다.정화 된 질문에 대한 응답으로

:

내가 __eq____cmp__에 preferecne에서 호출되는 것을 알고 있지만, y.__eq__(x)x.__eq__(y)에 우선이라고하는 이유는 명확하지 해요 때 후자 문서 상태가 일 것입니다.

문서는 x.__eq__(y)가 먼저 호출됩니다 말하지만,이 경우 y.__eq__(x)가 호출되는 NotImplemented를 반환 할 수있는 옵션이 있습니다. 왜 다른 일이 벌어지고 있는지 확신 할 수 없습니다.

어떤 경우에 특히 혼란 스럽습니까? "b" == tsctsc == "b" 사례에 대해 의아하게 생각하는 것이 맞습니까? 두 경우 모두 str.__eq__(onething, otherthing)이 호출됩니다. TestStrCmp에서 __eq__ 메서드를 재정의하지 않으므로 결국에는 기본 문자열 메서드에만 의존하게되고 개체가 동일하지 않다는 것을 의미합니다.

str.__eq__의 구현 세부 사항을 모른 채 ("b").__eq__(tsc)NotImplemented을 반환하고 동등성 테스트를 처리 할 수있는 기회가 있는지 여부를 알 수 없습니다. 그러나 TestStrCmp가 정의 된 방식대로 수행하더라도 여전히 잘못된 결과가 나타납니다.

예기치 않은 결과를 보았습니다.

아마도 무슨 일이 일어나고 있는지가 비교되는 객체 중 하나에 정의되어있는 경우에는 오른쪽 객체에 __eq__ 우선 순위를 가지고 가장 왼쪽의 객체에 __cmp__를 기대하고 있었던 반면, 파이썬, __cmp____eq__을 선호된다는 것이다. 그게 다야?

+0

이 작업을 조금 더 해본 결과, 두 경우 모두 '__eq__'가 선호됩니다. – Singletoned

1

알다시피, __eq__()은 소위 "리치 비교"방법이며 아래의 __cmp__()보다 우선하여 비교 연산자에 호출됩니다. 은 "리치 비교"가 정의되지 않은 경우 호출됩니다. 그래서 == B의

: __eq__()가에 정의되어
경우가 그렇지 __cmp__()
를 호출됩니다은 'STR'에 정의

__eq__()

를 호출 할 것입니다 귀하의 __cmp__() 함수가 호출되지 않았습니다.

동일한 규칙은 __ne__(), __gt__(), __ge__(), __lt__()__le__() "rich comparison"방법입니다.

6

실제로, docs에서는, 상태 :

비교 연산하여 비교 alled 리치 (위 참조)에 정의되어 있지 않은 경우 [__cmp__ 것은 C이다].

__eq__

TestCmp의 경우, 당신은 보통의 행동에 중요한 예외를 놓치고 __cmp__

+0

하지만'str ​​.__ eq__'가 정의되어 있으므로 아마도'TestStrCmp .__ eq__'가 정의 (상속)됩니다. – dubiousjim

+0

정확합니다. 나는 적절한 편집을했습니다 ... 감사합니다 – Dancrumb

+1

'__eq__'가'__cmp__'를 오버라이드한다는 것이 맞습니다. 그러나 그것은 놀라운 행동은 아닙니다. 놀랍게도 왼쪽 개체가 아니라 올바른 개체에서 호출하는 것이 었습니다. (나는 이것을 약간 명확하게하기 위해 질문을 업데이트했다). – Singletoned

관련 문제