2012-12-10 5 views
6

다음 코드와 혼동되는 Python 또는 Python입니까? 나는 __le__가되지 __ge__, a <= ab에 의해 호출 기대 : python bug with __le__, __ge__?

#!/usr/bin/env python2 

class B(object): 
    def __ge__(self, other): 
     print("__ge__ unexpectedly called") 

class A(object): 
    def __le__(self, other): 
     print("__le__ called") 

class AB(A, B): 
    pass 

a = A() 
ab = AB() 

a <= ab # --> __ge__ unexpectedly called 
ab <= a # --> __le__ called 

나는 파이썬 2.7, 3.2과 같은 동작을 얻을 1.9 pypy.

__ge__ 대신 __le__을 호출하려면 어떻게해야합니까 ??

+2

'a = A()'와'ab = AB()'를하고 같은 객체를 얻을 수있는 이유는 무엇입니까? (실제 프로그램에서이 프로그램이 유용 할 수있는 이유를 알 수 있지만,이 예제에는 전혀 영향을 미치지 않으며, 조사하고 희망적으로 대답하려는 사람들을 버릴 것입니다.) – abarnert

+0

실제로 단순화 된 버전을 제안합니다. 또한 같은 행동을합니다. 이제 수정되었습니다. – user474491

답변

8

짧은 대답은 ABA에서 동작을 재정의하도록하려는 것입니다. Python은 AB.__lt__(a, ab)을 호출 할 수 없습니다. aAB 메서드에서 유효한 self이 아니므로 AB.__gt__(ab, a)이라는 유효한 메서드를 호출합니다.

긴 대답은 좀 더 복잡합니다.

rich comparison operators의 문서에 따르면

이러한 방법에는 교환 인수 버전 (왼쪽 인자가이 조작을 지원하지 않지만 오른쪽 인자를 수행 할 때 사용되는)이 없다; 오히려 __lt__()__gt__()은 서로의 반사이며, __le__()__ge__()은 서로의 반사이며, __eq__()__ne__()은 자체 반영입니다. x+yy.__radd__(x)를 호출 할 경우 즉

, x <= y 정확히 같은 경우에 y.__ge__(x)를 호출합니다. 비교하려면

>>> class X(object): 
...  def __add__(self, other): 
...   print('X.add') 
>>> class Y(object): 
...  def __radd__(self, other): 
...   print('Y.radd') 
>>> class XY(X, Y): 
...  pass 
>>> x, xy = X(), XY() 
>>> x + xy 
Y.radd 

reflected operators의 문서에 따르면 반영이 방법은 ... 이진 산술 연산을 구현하기 위해 호출되는

피연산자 (교환). 왼쪽 피연산자가 해당 작업을 지원하지 않는 경우이 함수는 호출 및 피연산자는 다른 종류의 있습니다 ...

: 오른쪽 피연산자의 유형이 왼쪽 피연산자의 타입의 서브 클래스 인 경우 그 서브 클래스는 반사 제공 메서드를 호출하면이 메서드는 왼쪽 피연산자의 비 반영 메서드보다 먼저 호출됩니다. 이 동작을 통해 하위 클래스는 조상의 작업을 재정의 할 수 있습니다. XYX의 서브 클래스이기 때문에 그래서

XY.__radd__X.__add__보다 우선을 가져옵니다. 마찬가지로 ABA의 하위 클래스이므로 AB.__ge__A.__le__보다 우선합니다.

아마도 더 잘 문서화되어야합니다.그것을 파악하기 위해서는, "왼쪽 인수가 연산을 지원하지 않지만 오른쪽 인수는"사용할 괄호 안의 값을 무시해야합니다. 보통의 교환 된 연산자를 찾을 필요가 있다고 생각하십시오 (링크가 없거나 언급 할 필요도 없습니다). , 여기) 다음 "이 함수는 왼쪽 피연산자가 해당 연산을 지원하지 않는 경우에만 호출되며 위에 나온 것과 모순되는"참고 "를 참조하십시오. 또한 문서에서 명시 적으로"

마지막으로,이 경우이 이상한 것 같다

비교 연산자 ", 정확히 같은 관계를 의미하는 스왑 된 경우를 설명하기 전에 만 단락들 사이에 묵시적 관계 ..., 없다 AB 때문에, 오히려 자신을 __ge__를 오버라이드 (override)보다 단지에서 상속 B, 이는 거의 알지 못합니다. A과 관련이 없습니다. 아마도 B은 하위 클래스가 A의 동작을 재정의하도록 의도하지 않았습니다. 그러나 BA-derived 클래스에 대한 믹스 인으로 사용될 예정이면 과 정확히 일치 할 것입니다. 그리고 어쨌든이 규칙은 MRO에서 각 방법이 어디서 왔는지에 관계없이 이미 복잡 할 것입니다. 추론이 무엇이든간에, __ge__의 출처는 부적합합니다. 그것이 하위 클래스에 있으면 호출됩니다.

귀하의 추가 최종 질문에 대한

는, "나는 __le__ 대신 __ge__의 부르심을 얻기 위해 무엇을 할 수 있습니까 ??"... 음, 당신은 정말 당신이 대신 XY.__radd__의 부르심을 X.__add__를 얻을 수있는 것보다 더, 할 수 없습니다. 물론 A.__le__ (또는 X.__add__)을 호출하는 AB.__ge__ (또는 XY.__radd__)을 구현할 수 있지만 AB.__ge__을 처음 구현하면 다른 인수로 A과 작동하는 방식으로 구현하는 것이 더 쉽습니다. 또는 상속을 제거하고 모델링 할 때 다른 방식으로 모델을 만들 수 있습니다. 또는 a<=ab 대신 a.__le__(ab)을 명시 적으로 호출 할 수 있습니다. 하지만 그렇지 않은 경우, 이상한 일을하기 위해 "함축 된 관계가 없음"을 이용하는 방식으로 수업을 설계한다면, 당신은 문서에 속아 넘어졌고 어떻게 든 다시 디자인해야 할 것입니다.

+0

나는 그것을 안다.하지만'__le__'을 사용할 수있을 때 왜'__ge__'를 호출 할까 ?? – user474491

+1

"오른쪽 피연산자 유형이 왼쪽 피연산자 유형의 하위 클래스이고 그 하위 클래스가 반영된 메소드를 제공하기 때문에"하위 클래스가 상위 항목의 작업을 무시할 수 있기 때문입니다. (왜 AB가'B'에서'__ge__를 상속받은 것이지 직접적으로 정의하는 것보다 중요하다는 이유를 묻는다면 그것은 규칙에 의해 고려되지 않는다.). – abarnert

+2

@abarnert는 답변에서 (+1, BTW) 더 많은 파생 클래스 우선 순위를 제공하여 행동을 재정의 할 수 있도록 언급했습니다. – jimhark