
After perusing David Ascher's proposal, several versions of his patches, and hundreds of email exchanged on this subject (almost all of this dated April or May of 1998), I've produced a reasonably semblance of PEP 207. Get it from CVS or here on the web: http://python.sourceforge.net/peps/pep-0207.html I'd like to hear your comments, praise, and criticisms! The PEP still needs work; in particular, the minority point of view back then (that comparisons should return only Boolean results) is not adequately represented (but I *did* work in a reference to tabnanny, to ensure Tim's support :-). I'd like to work on a patch next, but I think there will be interference with Neil's coercion patch. I'm not sure how to resolve that yet; maybe I'll just wait until Neil's coercion patch is checked in. --Guido van Rossum (home page: http://www.python.org/~guido/)

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

This is only at the C level. Having to do a string compare is too slow. Since some of these are multi-character symbols, a character constant doesn't suffice (multi-character character constants are not portable).
I don't care about Fortran, but you just showed why I think the short operator names are better: there's less guessing or disagreement about how they are to be spelled. E.g. should it be __lessthan__ or __less_than__ or __less__?
It wouldn't make much of a difference -- comparisons between different types numbers would get the same outcome either way.
Sorry. I was thinking of list items that contain objects that respond to the new overloading protocol, but still return Boolean outcomes. My conclusion is that __cmp__ is just as well.
Maybe. That can still be decided later. Right now, adding operators is not on the table for 2.1 (if only because there are two conflicting PEPs); adding rich comparisons *is* on the table because it doesn't change the parser (and because the rich comparisons idea was already pretty much worked out two years ago). --Guido van Rossum (home page: http://www.python.org/~guido/)

Yes, it was worked out previously _assuming_ rich comparisons do not use any new operators. But let's stop for a moment and contemplate adding rich comparisons along with new comparison operators. What do we gain? 1. The current boolean operator behavior does not have to change, and hence will be backward compatible. 2. It eliminates the need to decide whether or not rich comparisons takes precedence over boolean comparisons. 3. The new operators add additional behavior without directly impacting current behavior and the use of them is unambigous, at least in relation to current Python behavior. You know by the operator what type of comparison will be returned. This should appease Jim Fulton, based on his arguments in 1998 about comparison operators always returning a boolean value. 4. Compound objects, such as lists, could implement both rich and boolean comparisons. The boolean comparison would remain as is, while the rich comparison would return a list of boolean values. Current behavior doesn't change; just a new feature, which you may or may not choose to use, is added. If we go one step further and add the matrix-style operators along with the comparison operators, we can provide a consistent user interface to array/complex operations without changing current Python behavior. If a user has no need for these new operators, he doesn't have to use them or even know about them. All we've done is made Python richer, but I believe with making it more complex. For example, all element-wise operations could have a ':' appended to them, e.g. '+:', '<:', etc.; and will define element-wise addition, element-wise less-than, etc. The traditional '*', '/', etc. operators can then be used for matrix operations, which will appease the Matlab people. Therefore, I don't think rich comparisons and matrix-type operators should be considered separable. I really think you should consider this suggestion. It appeases many groups while providing a consistent and clear user interface, while greatly impacting current Python behavior. Always-causing-havoc-at-the-last-moment-ly Yours, Paul -- Dr. Paul Barrett Space Telescope Science Institute Phone: 410-338-4475 ESS/Science Software Group FAX: 410-338-4767 Baltimore, MD 21218

Hi, Paul; thanks for your mail. W.r.t. adding matrix operators to Python, you may want to take a look at the counter-arguments in PEP 0211 (attached). Basically, I spoke with the authors of GNU Octave (a GPL'd clone of MATLAB) about what users really used. They felt that the only matrix operator that really mattered was matrix-matrix multiply; other operators (including the left and right division operators that even experienced MATLAB users often mix up) were second order at best, and were better handled with methods or functions. Thanks, Greg p.s. PEP 0225 (also attached) is an alternative to PEP 0211 which would add most of the MATLAB-ish operators to Python.

What incompatibility do you see in the current proposal?
2. It eliminates the need to decide whether or not rich comparisons takes precedence over boolean comparisons.
Only if you want different semantics -- that's only an issue for NumPy.
As you know, I'm now pretty close to Jim. :-) He seemed pretty mellow about this now.
I think you misunderstand. Rich comparisons are mostly about allowing the separate overloading of <, <=, ==, !=, >, and >=. This is useful in its own light. If you don't want to use this overloading facility for elementwise comparisons in NumPy, that's fine with me. Nobody says you have to -- it's just that you *could*. Red my lips: there won't be *any* new operators in 2.1. There will a better way to overload the existing Boolean operators, and they will be able to return non-Boolean results. That's useful in other situations besides NumPy. Feel free to lobby for elementwise operators -- but based on the discussion about this subject so far, I don't give it much of a chance even past Python 2.1. They would add a lot of baggage to the language (e.g. the table of operators in all Python books would be about twice as long) and by far the most users don't care about them. (Read the intro to 211 for some of the concerns -- this PEP tries to make the addition palatable by adding exactly *one* new operator.) --Guido van Rossum (home page: http://www.python.org/~guido/)

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

This is only at the C level. Having to do a string compare is too slow. Since some of these are multi-character symbols, a character constant doesn't suffice (multi-character character constants are not portable).
I don't care about Fortran, but you just showed why I think the short operator names are better: there's less guessing or disagreement about how they are to be spelled. E.g. should it be __lessthan__ or __less_than__ or __less__?
It wouldn't make much of a difference -- comparisons between different types numbers would get the same outcome either way.
Sorry. I was thinking of list items that contain objects that respond to the new overloading protocol, but still return Boolean outcomes. My conclusion is that __cmp__ is just as well.
Maybe. That can still be decided later. Right now, adding operators is not on the table for 2.1 (if only because there are two conflicting PEPs); adding rich comparisons *is* on the table because it doesn't change the parser (and because the rich comparisons idea was already pretty much worked out two years ago). --Guido van Rossum (home page: http://www.python.org/~guido/)

Yes, it was worked out previously _assuming_ rich comparisons do not use any new operators. But let's stop for a moment and contemplate adding rich comparisons along with new comparison operators. What do we gain? 1. The current boolean operator behavior does not have to change, and hence will be backward compatible. 2. It eliminates the need to decide whether or not rich comparisons takes precedence over boolean comparisons. 3. The new operators add additional behavior without directly impacting current behavior and the use of them is unambigous, at least in relation to current Python behavior. You know by the operator what type of comparison will be returned. This should appease Jim Fulton, based on his arguments in 1998 about comparison operators always returning a boolean value. 4. Compound objects, such as lists, could implement both rich and boolean comparisons. The boolean comparison would remain as is, while the rich comparison would return a list of boolean values. Current behavior doesn't change; just a new feature, which you may or may not choose to use, is added. If we go one step further and add the matrix-style operators along with the comparison operators, we can provide a consistent user interface to array/complex operations without changing current Python behavior. If a user has no need for these new operators, he doesn't have to use them or even know about them. All we've done is made Python richer, but I believe with making it more complex. For example, all element-wise operations could have a ':' appended to them, e.g. '+:', '<:', etc.; and will define element-wise addition, element-wise less-than, etc. The traditional '*', '/', etc. operators can then be used for matrix operations, which will appease the Matlab people. Therefore, I don't think rich comparisons and matrix-type operators should be considered separable. I really think you should consider this suggestion. It appeases many groups while providing a consistent and clear user interface, while greatly impacting current Python behavior. Always-causing-havoc-at-the-last-moment-ly Yours, Paul -- Dr. Paul Barrett Space Telescope Science Institute Phone: 410-338-4475 ESS/Science Software Group FAX: 410-338-4767 Baltimore, MD 21218

Hi, Paul; thanks for your mail. W.r.t. adding matrix operators to Python, you may want to take a look at the counter-arguments in PEP 0211 (attached). Basically, I spoke with the authors of GNU Octave (a GPL'd clone of MATLAB) about what users really used. They felt that the only matrix operator that really mattered was matrix-matrix multiply; other operators (including the left and right division operators that even experienced MATLAB users often mix up) were second order at best, and were better handled with methods or functions. Thanks, Greg p.s. PEP 0225 (also attached) is an alternative to PEP 0211 which would add most of the MATLAB-ish operators to Python.

What incompatibility do you see in the current proposal?
2. It eliminates the need to decide whether or not rich comparisons takes precedence over boolean comparisons.
Only if you want different semantics -- that's only an issue for NumPy.
As you know, I'm now pretty close to Jim. :-) He seemed pretty mellow about this now.
I think you misunderstand. Rich comparisons are mostly about allowing the separate overloading of <, <=, ==, !=, >, and >=. This is useful in its own light. If you don't want to use this overloading facility for elementwise comparisons in NumPy, that's fine with me. Nobody says you have to -- it's just that you *could*. Red my lips: there won't be *any* new operators in 2.1. There will a better way to overload the existing Boolean operators, and they will be able to return non-Boolean results. That's useful in other situations besides NumPy. Feel free to lobby for elementwise operators -- but based on the discussion about this subject so far, I don't give it much of a chance even past Python 2.1. They would add a lot of baggage to the language (e.g. the table of operators in all Python books would be about twice as long) and by far the most users don't care about them. (Read the intro to 211 for some of the concerns -- this PEP tries to make the addition palatable by adding exactly *one* new operator.) --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (3)
-
Greg Wilson
-
Guido van Rossum
-
Paul Barrett