__cmp__ 대신 __lt__


100

Python 2.x에는 비교 연산자를 오버로드하는 두 가지 방법 __cmp__또는 __lt__. 풍부한 비교 과부하가 선호된다고하지만 이것이 왜 그런가요?

풍부한 비교 연산자는 각각을 구현하는 것이 더 간단하지만 거의 동일한 논리로 여러 연산자를 구현해야합니다. 그러나 내장 cmp및 튜플 순서를 사용할 수 있다면 __cmp__매우 간단 해지고 모든 비교를 수행 할 수 있습니다 .

class A(object):
  def __init__(self, name, age, other):
    self.name = name
    self.age = age
    self.other = other
  def __cmp__(self, other):
    assert isinstance(other, A) # assumption for this example
    return cmp((self.name, self.age, self.other),
               (other.name, other.age, other.other))

이 단순함은 풍부한 비교 6 (!)을 모두 오버로드하는 것보다 훨씬 더 내 요구를 충족하는 것 같습니다. (그러나 "교체 된 주장"/ 반영된 행동에 의존한다면 "단지"4로 낮출 수 있지만, 내 겸손한 의견으로는 순전히 합병증이 증가합니다.)

과부하 만 발생하는 경우 알아야 할 예상치 못한 함정이 __cmp__있습니까?

나는 이해 <, <=, ==, 등 사업자가 다른 목적을 위해 오버로드 할 수 있으며, 원하는 개체에게 그들이 반환 할 수 있습니다. 나는 그 접근 방식의 장점에 대해 묻는 것이 아니라 숫자를 의미하는 것과 같은 의미에서 비교를 위해 이러한 연산자를 사용할 때의 차이점에 대해서만 묻습니다.

업데이트 : 크리스토퍼으로 지적 , cmp3.x에서에서 사라지고있다 위와 같이 쉽게 비교를 구현할 수있는 대안이 __cmp__있습니까?


5
마지막 질문에 대한 내 대답을 참조하십시오. 실제로 귀하를 포함한 많은 클래스에서 작업을 더 쉽게 만드는 디자인이 있습니다 (지금 적용하려면 믹스 인, 메타 클래스 또는 클래스 데코레이터가 필요합니다) : 특수 메서드가있는 경우 값의 튜플을 반환해야하며 모든 비교기 및 해시 는 해당 튜플을 기준으로 정의됩니다. Guido는 내가 그에게 설명했을 때 내 아이디어를 좋아했지만 다른 일로 바빴고 PEP를 작성하지 못했습니다 ... 어쩌면 3.2 ;-). 한편 나는 그 내 믹스 인을 계속 사용 -!)
알렉스 마르 텔리

답변:


90

예, 예 __lt__를 들어 mixin 클래스 (또는 메타 클래스, 또는 취향이 그런 식으로 실행되는 경우 클래스 데코레이터)의 관점에서 모든 것을 구현하는 것은 쉽습니다 .

예를 들면 :

class ComparableMixin:
  def __eq__(self, other):
    return not self<other and not other<self
  def __ne__(self, other):
    return self<other or other<self
  def __gt__(self, other):
    return other<self
  def __ge__(self, other):
    return not self<other
  def __le__(self, other):
    return not other<self

이제 클래스는 __lt__ComparableMixin에서 상속을 정의 하고 곱할 수 있습니다 (필요한 다른베이스가있는 경우). 클래스 데코레이터는 데코 레이트하는 새 클래스의 속성과 유사한 함수를 삽입하기 만하면 매우 유사 할 것입니다 (결과는 메모리 측면에서 동일한 분 비용으로 런타임에 미세하게 더 빠를 수 있음).

클래스가 구현하는 몇 가지 특히 빠른 방법 (예)가있는 경우 물론, __eq__그리고 __ne__, 그것은 믹스 인의 버전 (경우에 즉, 예를 들어, 사용하지 않도록 직접 정의해야합니다 dict- 사실) __ne__도 용이하게 정의 될 수있다 다음과 같이 :

def __ne__(self, other):
  return not self == other

그러나 위의 코드에서 나는 <;-) 만 사용하는 즐거운 대칭을 유지하고 싶었습니다 . 왜에 관해서는 __cmp__우리가 있기 때문에, 가야했다 했다__lt__친구, 왜, 주위 정확히 같은 일을 할 수있는 다른 방법을 다른 유지? 모든 Python 런타임 (Classic, Jython, IronPython, PyPy, ...)에서 매우 무겁습니다. 이라는 코드 확실히 버그가되지 않습니다이없는 코드입니다 - 거기에이 작업을 수행하기 위해 이상적으로 하나의 확실한 방법이 될한다고 그 파이썬의 원리는 (C는의 "성령 C의"절에 같은 원리를 가지고 어디서 ISO 표준, btw).

이것은 우리가 일을 금지하는 우리의 방법의 외출 의미하지 않는다 (예를 들어, 유지 mixin 어떤 용도 수준의 장식과 거의 동등한),하지만 확실히 않습니다 우리는 컴파일러와 코드를 들고 다니기 싫어한다는 것을 의미 / 또는 정확히 동일한 작업을 수행하는 여러 동등한 접근 방식을 지원하기 위해 중복 적으로 존재하는 런타임.

추가 편집 : 실제로 질문에 대한 __key__의견에서 언급했듯이 질문의 것을 포함하여 많은 클래스에 대한 비교 및 ​​해싱을 제공하는 더 나은 방법이 있습니다 . PEP를 작성 해본 적이 없기 때문에 현재 원하는 경우 Mixin (& c)을 사용하여 구현해야합니다.

class KeyedMixin:
  def __lt__(self, other):
    return self.__key__() < other.__key__()
  # and so on for other comparators, as above, plus:
  def __hash__(self):
    return hash(self.__key__())

인스턴스를 다른 인스턴스와 비교하는 것은 몇 개의 필드가있는 각각의 튜플을 비교하는 것으로 요약되는 매우 일반적인 경우이며 해싱은 정확히 동일한 기준으로 구현되어야합니다. __key__직접해야 할 특별한 방법 주소.


@R 지연에 대해 죄송합니다. Pate, 나는 어쨌든 편집해야했기 때문에 서두르 기보다는 내가 할 수있는 가장 철저한 대답을 제공해야한다고 결정했습니다 (그리고 나는 PEPping에 대해 결코 이해하지 못했던 나의 오래된 핵심 아이디어 를 제안하기 위해 다시 편집했습니다. mixin으로 구현).
Alex Martelli

저는 그 핵심 아이디어가 정말 마음에 듭니다. 그것을 사용하고 그것이 어떤 느낌인지 볼 것입니다. (예약 된 이름 대신 cmp_key 또는 _cmp_key로 이름이 지정되었지만)

TypeError: Cannot create a consistent method resolution order (MRO) for bases object, ComparableMixinPython 3에서이 작업을 시도 할 때 전체 코드는 gist.github.com/2696496
Adam Parkin

2
파이썬 2.7에서 +는 / 3.2 + 당신이 사용할 수있는 functools.total_ordering대신에 자신의 건물 ComparableMixim. jmagnusson의 답변
Day

4
Python 3에서 <구현하는 데 사용 하는 __eq__것은 TypeError: unorderable types.
Antti Haapala

49

이 경우를 단순화하기 위해 Alex가 제안한 것을 구현하는 데 사용할 수있는 Python 2.7 + / 3.2 +, functools.total_ordering 의 클래스 데코레이터 가 있습니다. 문서의 예 :

@total_ordering
class Student:
    def __eq__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))

9
total_ordering__ne__그래도 구현되지 않으므로 조심하십시오!
Flimm 2013-04-04

3
@Flimm, 그렇지 않지만 __ne__. 하지만 그 이유 __ne____eq__. 그래서 여기서 조심할 것이 없습니다.
Jan Hudec

적어도 하나의 주문 작업을 정의해야합니다. <> <=> = .... eq는 만약! a <b 및 b <a then a = b
Xanlantos

9

이것은 PEP 207-Rich Comparisons에서 다룹니다.

또한 __cmp__파이썬 3.0에서는 사라집니다. (참고 그것은에 존재하지 않는다고 http://docs.python.org/3.0/reference/datamodel.html 하지만 켜져 http://docs.python.org/2.7/reference/datamodel.html )


PEP는 NumPy 사용자가 A <B가 시퀀스를 반환하기를 원하는 방식으로 풍부한 비교가 필요한 이유에만 관심이 있습니다.

나는 그것이 확실히 사라지고 있다는 것을 깨닫지 못했다. 이것은 나를 슬프게 만든다. (하지만 지적 해 주셔서 감사합니다.)

PEP는 또한 그들이 선호하는 "이유"에 대해서도 논의합니다. 본질적으로 효율성으로 귀결됩니다. 1. 객체에 맞지 않는 작업을 구현할 필요가 없습니다 (예 : 정렬되지 않은 컬렉션). 2. 일부 컬렉션은 어떤 종류의 비교에서 매우 효율적인 작업을 수행합니다. 풍부한 비교를 통해 인터프리터는이를 정의 할 수 있습니다.
Christopher

1
Re 1, 그들이 말이 안된다면 cmp를 구현하지 마십시오 . 다시 2, 두 옵션을 모두 사용하면 필요에 따라 최적화하는 동시에 빠르게 프로토 타이핑 및 테스트 할 수 있습니다. 어느 쪽도 그것이 왜 제거되었는지 말해주지 않습니다. (본질적으로 개발자의 효율성으로 귀결됩니다.) cmp 폴백을 사용하면 풍부한 비교가 덜 효율적일 수 있습니까? 그건 말이 안 돼요.

1
@아르 자형. Pate, 내 대답에서 설명하려고 할 때 일반성에는 실제 손실이 없습니다 (믹스 인, 데코레이터 또는 메타 클래스를 사용하면 원하는 경우 <으로 모든 것을 쉽게 정의 할 수 있기 때문에) 모든 Python 구현이 수행 될 수 있습니다. 파이썬 사용자가 두 가지 동등한 방법으로 사물을 표현할 수 있도록 하는 중복 코드는 영원히 cmp로 돌아갈 것입니다. 파이썬 단위에 대해 100 % 실행됩니다.
Alex Martelli

2

(댓글을 고려하여 17 년 6 월 17 일에 수정 됨)

위의 비슷한 mixin 답변을 시도했습니다. "없음"에 문제가 생겼습니다. 다음은 "None"과 동등 비교를 처리하는 수정 된 버전입니다. (나는 의미가 부족한 None과의 불평등 비교에 신경을 쓸 이유가 없다고 보았다) :


class ComparableMixin(object):

    def __eq__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
        else:
            return not self<other and not other<self

    def __ne__(self, other):
        return not __eq__(self, other)

    def __gt__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
        else:
            return other<self

    def __ge__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
        else:
            return not self<other

    def __le__(self, other):
        if not isinstance(other, type(self)): 
            return NotImplemented
        else:
            return not other<self    

당신은 어떻게 생각하십니까 self싱글 수 NoneNoneType당신의 구현 같은 시간에 ComparableMixin? 그리고 실제로이 조리법은 파이썬 3에 대한 나쁜
안티 Haapala

3
self것이다 결코 없을 None해당 분기가 완전히 갈 수 있습니다. 사용하지 마십시오 type(other) == type(None). 간단히 other is None. 특수한 None경우가 아니라 다른 유형이 유형의 인스턴스인지 테스트 self하고 NotImplemented그렇지 않은 경우 싱글 톤을 반환합니다 : if not isinstance(other, type(self)): return NotImplemented. 모든 방법에 대해이 작업을 수행하십시오. 그러면 파이썬은 다른 피연산자에게 대신 답을 제공 할 기회를 줄 수 있습니다.
Martijn Pieters

1

Alex Martelli의 ComparableMixin& KeyedMixin답변에 영감을 받아 다음 믹스 인을 생각해 냈습니다. 이를 통해 _compare_to()와 유사한 키 기반 비교를 사용 하는 단일 메서드 를 구현할 KeyedMixin수 있지만 클래스는 유형에 따라 가장 효율적인 비교 키를 선택할 수 있습니다 other. (이 믹스 인은 동등성을 테스트 할 수 있지만 순서가 아닌 객체에 대해서는별로 도움이되지 않습니다.)

class ComparableMixin(object):
    """mixin which implements rich comparison operators in terms of a single _compare_to() helper"""

    def _compare_to(self, other):
        """return keys to compare self to other.

        if self and other are comparable, this function 
        should return ``(self key, other key)``.
        if they aren't, it should return ``None`` instead.
        """
        raise NotImplementedError("_compare_to() must be implemented by subclass")

    def __eq__(self, other):
        keys = self._compare_to(other)
        return keys[0] == keys[1] if keys else NotImplemented

    def __ne__(self, other):
        return not self == other

    def __lt__(self, other):
        keys = self._compare_to(other)
        return keys[0] < keys[1] if keys else NotImplemented

    def __le__(self, other):
        keys = self._compare_to(other)
        return keys[0] <= keys[1] if keys else NotImplemented

    def __gt__(self, other):
        keys = self._compare_to(other)
        return keys[0] > keys[1] if keys else NotImplemented

    def __ge__(self, other):
        keys = self._compare_to(other)
        return keys[0] >= keys[1] if keys else NotImplemented
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.