[Python-3000] Trying to understand Python 3's comparisons
Jim Jewett
jimjjewett at gmail.com
Tue Nov 20 21:01:58 CET 2007
(top-posting to shorten the reading time)
Note that in 2.5
>>> Eq("b") < Eq("a")
True
Your Eq class didn't define a __lt__, so in 2.5, it inherits the
default method, and ends up (in CPython) looking at the object's
address, so the first-created object will be less. In this case, it
seemed to do the right thing. In python 3, that fallback is gone, and
uncomparable things can't be ordered.
More details:
Python treats the comparisons as almost[*] arbitrary binary
operations. There are even some hacks that take advantage of this to
use funky notation.
This does mean that it doesn't fall back on logical equivalences. It
is entirely possible (though pathological) for two objects to be both
equal and unequal, or for one to be both less-than the other but not
less-than-or-equal.
[*]There are two exceptions to the "arbitrary functions" rule. If an
operation is not defined by the left operand, python will try to the
reversed operation from the right operand. So when you asked for
"a>b" you got forwarded to "b<a". There is also a shortcut so that "a
is b" ==> (a ==b and not a != b)
So why is python 3 different from python 2? Because in python 2, your
custom object inherited default behavior that let anything be ordered
(unless it took steps to avoid that). That default ends up looking at
object address, which is why the first-created object is less. In
python 3, there is no such default.
-jJ
On 11/16/07, Mark Summerfield <mark at qtrac.eu> wrote:
> Hi,
>
> I'm trying to understand Python 3's comparisons.
>
> class Eq:
> def __init__(self, x=""):
> self.x = x
> def __str__(self):
> return self.x
> def __eq__(self, other):
> return str(self) == str(other)
>
> class Lt:
> def __init__(self, x=""):
> self.x = x
> def __str__(self):
> return self.x
> def __lt__(self, other):
> return str(self) < str(other)
>
> class LtEq:
> def __init__(self, x=""):
> self.x = x
> def __str__(self):
> return self.x
> def __eq__(self, other):
> return str(self) == str(other)
> def __lt__(self, other):
> return str(self) < str(other)
>
> pairs = ((Eq("a"), Eq("b")), (Lt("a"), Lt("b")), (LtEq("a"), LtEq("b")))
> for a, b in pairs:
> print("comparing", type(a))
> try:
> print("a < b", a < b)
> except TypeError as err: # TypeError, err in Python 2
> print(err)
> # etc, for all the other comparisons
>
> For Python 2 I get this output:
>
> comparing <type 'instance'> # Eq
> a < b True
> a <= b True
> a == b False
> a != b True
> a > b False
> a >= b False
>
> comparing <type 'instance'> # Lt
> a < b True
> a <= b True
> a == b False
> a != b True
> a > b False
> a >= b False
>
> comparing <type 'instance'> #LtEq
> a < b True
> a <= b True
> a == b False
> a != b True
> a > b False
> a >= b False
>
> Clearly this is bad since class Eq has no ordering and class Lt has no
> notion of equality.
>
> For Python 3 I get this output:
>
> comparing <class '__main__.Eq'>
> unorderable types: Eq() < Eq()
> unorderable types: Eq() <= Eq()
> a == b False
> a != b True
> unorderable types: Eq() > Eq()
> unorderable types: Eq() >= Eq()
>
> comparing <class '__main__.Lt'>
> a < b True
> unorderable types: Lt() <= Lt()
> a == b False
> a != b True
> a > b False
> unorderable types: Lt() >= Lt()
>
> comparing <class '__main__.LtEq'>
> a < b True
> unorderable types: LtEq() <= LtEq()
> a == b False
> a != b True
> a > b False
> unorderable types: LtEq() >= LtEq()
>
> This is much better in the case of classes Eq and Lt. But I don't
> understand why class LtEq does not handle <= or =>. Bear in mind that
> for class Eq I only defined ==, Python 3 created != for me; similarly
> for class Lt I defined < and Python created >. Or is my code for LtEq
> wrong?
>
> I know it isn't a problem creating a class decorator or metaclass to
> "complete" LtEq; I'm just trying to understand how Python 3 comparisons
> work.
>
> Thanks!
More information about the Python-3000
mailing list