Python 3 __cmp__ semantic change?

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Fri Nov 21 20:12:48 EST 2008


On Fri, 21 Nov 2008 17:26:21 +0000, Arnaud Delobelle wrote:

[...]
> As classes can be decorated in Python 3, you can write a decorator to
> make a class totally ordered.  Here is a very simplified proof of
> concept such decorator:
> 
> def totally_ordered(cls):
>     if not hasattr(cls, '__gt__'):
>         def gt(self, other):
>             return self != other and not self < other
>         cls.__gt__ = gt
>     # Do the same with __le__, __ge__
>     return cls
> 
> 
> @totally_ordered
> class Fraction:
>     def __init__(self, num, den=1):
>         assert den > 0, "denomintator must be > 0" self.num = num
>         self.den = den
>     def __eq__(self, other):
>         return self.num*other.den == self.den*other.num
>     def __lt__(self, other):
>         return self.num*other.den < self.den*other.num
> 
>>>> q12=Fraction(1, 2)
>>>> q23=Fraction(2, 3)
>>>> q12 < q23
> True
>>>> q12 > q23
> False
> 
> Granted it's not as efficient as a __cmp__ function.

What makes you say that? What do you mean by "efficient"? Are you talking 
about memory footprint, runtime speed, disk-space, programmer efficiency, 
algorithmic complexity, or something else?

As I see it, a __cmp__ method would be written something like this:

    def __cmp__(self, other):
        return cmp(self.num*other.den, self.den*other.num)

which presumably would save you a trivial amount of source code (and 
hence memory footprint, disk-space and programmer efficiency), but the 
algorithmic complexity is identical and the runtime speed might even be 
trivially slower due to the extra function call.

If your major concern is to reduce the amount of repeated code in the 
methods, then there's no reason why you can't write a __cmp__ method as 
above and then call it from your rich comparisons:

    def __eq__(self, other):
        return self.__cmp__(other) == 0
    def __lt__(self, other):
        return self.__cmp__(other) < 0

and if you really want to be concise:

    __gt__ = lambda s, o: s.__cmp__(o) > 0
    __ge__ = lambda s, o: s.__cmp__(o) >= 0
    __le__ = lambda s, o: s.__cmp__(o) <= 0





-- 
Steven



More information about the Python-list mailing list