Python 3 __cmp__ semantic change?

Arnaud Delobelle arnodel at googlemail.com
Sun Nov 23 05:05:27 EST 2008


Steven D'Aprano <steve at REMOVE-THIS-cybersource.com.au> writes:

> 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.

All I was asserting was that using my decorator, Fraction.__gt__ would
be roughly twice as slow as Fraction.__eq__ or Fraction.__lt__.  I was
not worried about it at all!  Your tests below, although very
interesting, don't shed any light on this.

> (BTW, I think your earlier decorator had a bug, in that it failed to
> define __ne__ but then called "self != other".)

That would be true for Python 2.x but I'm explicitly writing code for
Python 3 here, which, IIRC, infers != correctly when you define ==.  I
can't refer you to the docs because my internet access to some US sites
seems to be partly broken ATM, but here's a simple example:

Python 3.0rc1+ (py3k:66521, Sep 21 2008, 07:58:29) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class A:
...     def __init__(self):
...         self
... 
>>> 
>>> class A:
...     def __init__(self, x):
...         self.x = x
...     def __eq__(self, other):
...         return self.x == other.x
... 
>>> a, b, c = A(1), A(1), A(2)
>>> a==b, b==c, c==a
(True, False, False)
>>> a!=b, b!=c, c!=a
(False, True, True)
>>> 

> 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.

[snip test code]

If anything these tests make a retroactive case for getting rid of
__cmp__ as it seems really slow.  Even FractionRichCmpIndirect (which is
the same as applying my second totally_ordered decorator to FractionCmp)
is almost twice as fast as FractionCmp.  I would guess it is because
when doing e.g.

    a < b

The VM will first look for

    a.__lt__(b)

and fail, wasting some time in the process.  Then it would look for

    a.__cmp__(b)

Whereas when __lt__ is explicitely defined, the first step always
succeeds.

-- 
Arnaud




More information about the Python-list mailing list