Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

container.___le___ can use only <=?

4 views
Skip to first unread message

Neil Cerutti

unread,
Dec 14, 2007, 4:15:44 PM12/14/07
to
When implementing the rich comparison operators for some sort of
container, it's tempting to save code by doing something like:

class LarchTree:
...
def __gt__(self, other):
# A lot of code to traverse the tree
def __le__(self):
return not self > other

However, if I'm thinking correctly, this is a bad idea. The
reasoning being that > and <= are not required to be each others
opposite for arbitrary value types, and may not even both be
supplied by the contained objects.

If a LarchTree user stores objects that don't support __gt__,
will he have a legitimate complaint when using LarchTree.__le__
results in an attribute error?

--
Neil Cerutti

Steven D'Aprano

unread,
Dec 14, 2007, 11:04:31 PM12/14/07
to

I'm not sure that an AttributeError is the right exception to raise, but
in general I'd say that if the contained objects don't support "normal"
comparisons (that is, if < and >= aren't opposites, etc.) then all bets
are off. "Behaviour is undefined" time.

BTW, what is a LarchTree? Googling just brings up the actual botanical
tree, a type of conifer.

http://en.wikipedia.org/wiki/Larch


If you haven't already done so, you should consider emulating the
behaviour of lists, and only raise an error if it is absolutely necessary:

>>> L1 = [35, 23+42j]
>>> L2 = [37, 18]
>>> L1 <= L2
True
>>> L1 = [23+42j, 35]
>>> L1 <= L2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: no ordering relation is defined for complex numbers

--
Steven

cer...@tds.net

unread,
Dec 15, 2007, 7:54:52 AM12/15/07
to
On Dec 14, 11:04 pm, Steven D'Aprano <st...@REMOVE-THIS-

cybersource.com.au> wrote:
> On Fri, 14 Dec 2007 21:15:44 +0000, Neil Cerutti wrote:
> > When implementing the rich comparison operators for some sort of
> > container, it's tempting to save code by doing something like:
>
> > class LarchTree:
> > ...
> > def __gt__(self, other):
> > # A lot of code to traverse the tree
> > def __le__(self):
> > return not self > other
>
> > However, if I'm thinking correctly, this is a bad idea. The reasoning
> > being that > and <= are not required to be each others opposite for
> > arbitrary value types, and may not even both be supplied by the
> > contained objects.
>
> > If a LarchTree user stores objects that don't support __gt__, will he
> > have a legitimate complaint when using LarchTree.__le__ results in an
> > attribute error?
>
> I'm not sure that an AttributeError is the right exception to raise, but
> in general I'd say that if the contained objects don't support "normal"
> comparisons (that is, if < and >= aren't opposites, etc.) then all bets
> are off. "Behaviour is undefined" time.

I wasn't going to raise the exception, I was thinking about calling a
function that raised the exception.

Assume for a moment that list's __le__ were implemented as above.

class BadGT(object):
def __gt__(self, o):
raise RuntimeError("I just hate this operation")
def __le__(self, o):
return id(self) <= id(other)

>>> x = [BadGT(), BadGT()]
>>> x <= []

If lists implementation of __le__ calls __gt__ on its constituents
*then* it'll be a RuntimeError.

I've tested list, and it safely compares lists containing BadGT
objects, as along I don't call __gt__ myself. So I was contemplating
implementing a phalanx of tests along these lines, which the current
version of my container will surely fail.

> BTW, what is a LarchTree? Googling just brings up the actual botanical
> tree, a type of conifer.
>
> http://en.wikipedia.org/wiki/Larch

It was a Monty Python reference. I'm actually still fiddling around
with a doubly-linked list implementation. It's kinda embarrassing so I
hid that fact, but actually I'm learning a bunch of Python and getting
design practice from this exercise (it turns out linked lists have
keys!).

--
Neil Cerutti

Carl Banks

unread,
Dec 15, 2007, 8:33:50 AM12/15/07
to
On Dec 14, 4:15 pm, Neil Cerutti <horp...@yahoo.com> wrote:
> When implementing the rich comparison operators for some sort of
> container, it's tempting to save code by doing something like:
>
> class LarchTree:
> ...
> def __gt__(self, other):
> # A lot of code to traverse the tree
> def __le__(self):
> return not self > other
>
> However, if I'm thinking correctly, this is a bad idea. The
> reasoning being that > and <= are not required to be each others
> opposite for arbitrary value types, and may not even both be
> supplied by the contained objects.

And yet, by the same reasoning, using > and <= for list and tuple is
also a "bad idea".


> If a LarchTree user stores objects that don't support __gt__,
> will he have a legitimate complaint when using LarchTree.__le__
> results in an attribute error?

No.


Carl Banks

cer...@tds.net

unread,
Dec 15, 2007, 9:05:10 AM12/15/07
to
On Dec 15, 8:33 am, Carl Banks <pavlovevide...@gmail.com> wrote:
> On Dec 14, 4:15 pm, Neil Cerutti <horp...@yahoo.com> wrote:
>
> > When implementing the rich comparison operators for some sort of
> > container, it's tempting to save code by doing something like:
>
> > class LarchTree:
> > ...
> > def __gt__(self, other):
> > # A lot of code to traverse the tree
> > def __le__(self):
> > return not self > other
>
> > However, if I'm thinking correctly, this is a bad idea. The
> > reasoning being that > and <= are not required to be each others
> > opposite for arbitrary value types, and may not even both be
> > supplied by the contained objects.
>
> And yet, by the same reasoning, using > and <= for list and tuple is
> also a "bad idea".

My reasoning is (I hope) that the container ought to support every
comparison operation supported by the contained objects. This can be
ensured by being careful in the implementation.

--
Neil Cerutti

Arnaud Delobelle

unread,
Dec 15, 2007, 9:25:56 AM12/15/07
to
On Dec 15, 2:05 pm, ceru...@tds.net wrote:

> My reasoning is (I hope) that the container ought to support every
> comparison operation supported by the contained objects. This can be
> ensured by being careful in the implementation.
>

If you're traversing two containers in parallel to compare them then
you could do something like:

class LarchTree:
...
def compare(self, other, cmp):
# Same code as your original __gt__ but
# with cmp(x, y) instead of x > y

def __lt__(self, other):
return self.compare(other, operator.lt)

def __gt__(self, other):
return self.compare(other, operator.gt)

# etc...


This way the LarchTree object doesn't interpret the comparison
operators, just passes them on to its elements.

--
Arnaud

Carl Banks

unread,
Dec 15, 2007, 9:39:52 AM12/15/07
to

I see what you're asking now. Yep, probably a bad idea to use that
shortcut.


Carl Banks

Neil Cerutti

unread,
Dec 19, 2007, 11:03:27 AM12/19/07
to

I put together a test suite, and discovered that the built-in
list assumes that all contained object support __eq__, i.e., it
may call __eq__ while performing some other comparison, e.g.,
__gt__. This is contrary to the rule I was contemplating for
sequence comparisons in this thread.

>>> class Evil:
... def __init__(self, i):
... self.i = i
... def __eq__(self, o):
... raise RuntimeError
... def __gt__(self, o):
... if isinstance(o, Evil):
... return self.i > o.i
... return NotImplemented
...
>>> [Evil(1)] > [Evil(0)]


Traceback (most recent call last):
File "<stdin>", line 1, in <module>

File "<stdin>", line 5, in __eq__
RuntimeError

This prompted inspection of listobject.c. After trying some
short-cuts for __ne__ and __eq__, list_richcompare iterates
through both lists until the first non-equal elements are found,
and then performs the correct comparison, or simply returns,
depending on if the lists were the same length.

Otherwise, the built-in list rich comparison follows the advice
posted in this thread, i.e., it uses only the comparison
operation being implemented to compare contained objects.

When implementing a mutable sequence in Python, the rule of thumb
seems to be "do as lists do," so as of the Python's current
implementation, there's a small limit on the perversity level of
the rich comparison operations of types contained in lists.

--
Neil Cerutti

0 new messages