developer tip

__cmp__ 대신 __lt__

optionbox 2020. 8. 27. 07:41
반응형

__cmp__ 대신 __lt__


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__있습니까?


예, 예 __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).

This doesn't mean we go out of our way to prohibit things (e.g., near-equivalence between mixins and class decorators for some uses), but it definitely does mean that we don't like to carry around code in the compilers and/or runtimes that redundantly exists just to support multiple equivalent approaches to perform exactly the same task.

Further edit: there's actually an even better way to provide comparison AND hashing for many classes, including that in the question -- a __key__ method, as I mentioned on my comment to the question. Since I never got around to writing the PEP for it, you must currently implement it with a Mixin (&c) if you like it:

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__())

It's a very common case for an instance's comparisons with other instances to boil down to comparing a tuple for each with a few fields -- and then, hashing should be implemented on exactly the same basis. The __key__ special method addresses that need directly.


To simplify this case there's a class decorator in Python 2.7+/3.2+, functools.total_ordering, that can be used to implement what Alex suggests. Example from the docs:

@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()))

This is covered by PEP 207 - Rich Comparisons

Also, __cmp__ goes away in python 3.0. ( Note that it is not present on http://docs.python.org/3.0/reference/datamodel.html but it IS on http://docs.python.org/2.7/reference/datamodel.html )


Inspired by Alex Martelli's ComparableMixin & KeyedMixin answers, I came up with the following mixin. It allows you to implement a single _compare_to() method, which uses key-based comparisons similar to KeyedMixin, but allows your class to pick the most efficient comparison key based on the type of other. (Note that this mixin doesn't help much for objects which can be tested for equality but not order).

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

(Edited 6/17/17 to take comments into account.)

I tried out the comparable mixin answer above. I ran into trouble with "None". Here is a modified version that handles equality comparisons with "None". (I saw no reason to bother with inequality comparisons with None as lacking semantics):


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    

참고URL : https://stackoverflow.com/questions/1061283/lt-instead-of-cmp

반응형