[Python-Dev] PEP 207 -- Rich Comparisons
Paul Barrett
Barrett@stsci.edu
Fri, 15 Dec 2000 14:32:10 -0500 (EST)
Guido,
Here are my comments on PEP 207. (I've also gone back and read most
of the 1998 discussion. What a tedious, in terms of time, but
enlightening, in terms of content, discussion that was.)
| - New function:
|
| PyObject *PyObject_RichCompare(PyObject *, PyObject *, enum cmp_op)
|
| This performs the requested rich comparison, returning a Python
| object or raising an exception. The 3rd argument must be one of
| LT, LE, EQ, NE, GT or GE.
I'd much prefer '<', '<=', '=', etc. to LT, LE, EQ, etc.
| Classes
|
| - Classes can define new special methods __lt__, __le__, __gt__,
| __ge__, __eq__, __ne__ to override the corresponding operators.
| (You gotta love the Fortran heritage.) If a class overrides
| __cmp__ as well, it is only used by PyObject_Compare().
Likewise, I'd prefer __less__, __lessequal__, __equal__, etc. to
__lt__, __le__, __eq__, etc. I'm not keen on the FORTRAN derived
symbolism. I also find it contrary to Python's heritage of being
clear and concise. I don't mind typing __lessequal__ (or
__less_equal__) once per class for the additional clarity.
| - Should we even bother upgrading the existing types?
Isn't this question partly related to the coercion issue and which
type of comparison takes precedence? And if so, then I would think
the answer would be 'yes'. Or better still see below my suggestion of
adding poor and rich comparison operators along with matrix-type
operators.
- If so, how should comparisons on container types be defined?
Suppose we have a list whose items define rich comparisons. How
should the itemwise comparisons be done? For example:
def __lt__(a, b): # a<b for lists
for i in range(min(len(a), len(b))):
ai, bi = a[i], b[i]
if ai < bi: return 1
if ai == bi: continue
if ai > bi: return 0
raise TypeError, "incomparable item types"
return len(a) < len(b)
This uses the same sequence of comparisons as cmp(), so it may
as well use cmp() instead:
def __lt__(a, b): # a<b for lists
for i in range(min(len(a), len(b))):
c = cmp(a[i], b[i])
if c < 0: return 1
if c == 0: continue
if c > 0: return 0
assert 0 # unreachable
return len(a) < len(b)
And now there's not really a reason to change lists to rich
comparisons.
I don't understand this example. If a[i] and b[i] define rich
comparisons, then 'a[i] < b[i]' is likely to return a non-boolean
value. Yet the 'if' statement expects a boolean value. I don't see
how the above example will work.
This example also makes me think that the proposals for new operators
(ie. PEP 211 and 225) are a good idea. The discussion of rich
comparisons in 1998 also lends some support to this. I can see many
uses for two types of comparison operators (as well as the proposed
matrix-type operators), one set for poor or boolean comparisons and
one for rich or non-boolean comparisons. For example, numeric arrays
can define both. Rich comparison operators would return an array of
boolean values, while poor comparison operators return a boolean value
by performing an implied 'and.reduce' operation. These operators
provide clarity and conciseness, without much change to current Python
behavior.
-- Paul