On Sat, 22 Nov 2008 09:10:04 +0000, Arnaud Delobelle wrote: > That's not surprising. You're measuring the wrong things. If you read > what I wrote, you'll see that I'm talking about Fraction.__gt__ being > slower (as it is defined in terms of Fraction.__eq__ and > Fraction.__lt__) using when my 'totally_ordered' decorator. > > I haven't done any tests but as Fraction.__gt__ calls *both* > Fraction.__eq__ and Fraction.__lt__ it is obvious that it is going to be > roughly twice as slow.
What's obvious to you and what's obvious to the Python VM are not necessarily the same thing. I believe you are worrying about the wrong thing. (BTW, I think your earlier decorator had a bug, in that it failed to define __ne__ but then called "self != other".) My tests suggest that relying on __cmp__ is nearly three times *slower* than your decorated class, and around four times slower than defining all the rich comparisons directly: $ python comparisons.py Testing FractionCmp... 37.4376080036 Testing FractionRichCmpDirect... 9.83379387856 Testing FractionRichCmpIndirect... 16.152534008 Testing FractionDecoratored... 13.2626030445 Test code follows. If I've made an error, please let me know. [comparisons.py] from __future__ import division def totally_ordered(cls): # Define total ordering in terms of __eq__ and __lt__ only. if not hasattr(cls, '__ne__'): def ne(self, other): return not self == other cls.__ne__ = ne if not hasattr(cls, '__gt__'): def gt(self, other): return not (self < other or self == other) cls.__gt__ = gt if not hasattr(cls, '__ge__'): def ge(self, other): return not (self < other) cls.__ge__ = ge if not hasattr(cls, '__le__'): def le(self, other): return (self < other or self == other) cls.__le__ = le return cls class AbstractFraction: def __init__(self, num, den=1): if self.__class__ is AbstractFraction: raise TypeError("abstract base class, do not instantiate") assert den > 0, "denomintator must be > 0" self.num = num self.den = den def __float__(self): return self.num/self.den class FractionCmp(AbstractFraction): def __cmp__(self, other): return cmp(self.num*other.den, self.den*other.num) class FractionRichCmpDirect(AbstractFraction): def __eq__(self, other): return (self.num*other.den) == (self.den*other.num) def __ne__(self, other): return (self.num*other.den) != (self.den*other.num) def __lt__(self, other): return (self.num*other.den) < (self.den*other.num) def __le__(self, other): return (self.num*other.den) <= (self.den*other.num) def __gt__(self, other): return (self.num*other.den) > (self.den*other.num) def __ge__(self, other): return (self.num*other.den) >= (self.den*other.num) class FractionRichCmpIndirect(AbstractFraction): def __cmp__(self, other): return cmp(self.num*other.den, self.den*other.num) def __eq__(self, other): return self.__cmp__(other) == 0 def __ne__(self, other): return self.__cmp__(other) != 0 def __lt__(self, other): return self.__cmp__(other) < 0 def __le__(self, other): return self.__cmp__(other) <= 0 def __gt__(self, other): return self.__cmp__(other) > 0 def __ge__(self, other): return self.__cmp__(other) >= 0 class FractionDecoratored(AbstractFraction): 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 FractionDecoratored = totally_ordered(FractionDecoratored) def test_suite(small, big): assert small < big assert small <= big assert not (small > big) assert not (small >= big) assert small != big assert not (small == big) from timeit import Timer test = 'test_suite(p, q)' setup = '''from __main__ import %s as frac from __main__ import test_suite p = frac(1, 2) q = frac(4, 5) assert float(p) < float(q)''' for cls in [FractionCmp, FractionRichCmpDirect, FractionRichCmpIndirect, FractionDecoratored]: t = Timer(test, setup % cls.__name__) print "Testing %s..." % cls.__name__, best = min(t.repeat()) print best [end comparisons.py] -- Steven -- http://mail.python.org/mailman/listinfo/python-list