[Python-Dev] python 3 niggle: None < 1 raises TypeError

Terry Reedy tjreedy at udel.edu
Mon Feb 17 16:29:14 CET 2014

On 2/17/2014 7:25 AM, M.-A. Lemburg wrote:
> On 17.02.2014 13:12, Nick Coghlan wrote:
>> On 17 Feb 2014 21:15, "M.-A. Lemburg" <mal at egenix.com> wrote:
>>> On 15.02.2014 07:03, Stephen J. Turnbull wrote:
>>>> M.-A. Lemburg writes:
>>>>   > IMO, it was a mistake to have None return a TypeError in
>>>>   > comparisons, since it makes many typical data operations
>>>>   > fail, e.g.
>>>> I don't understand this statement.  The theory is that they *should*
>>>> fail.
>>>> The example of sort is a good one.  Sometimes you want missing values
>>>> to be collected at the beginning of a list, sometimes at the end.
>>>> Sometimes you want them treated as top elements, sometimes as bottom.
>>>> And sometimes it is a real error for missing values to be present.
>>>> Not to mention that sometimes the programmer simply hasn't thought
>>>> about the appropriate policy.  I don't think Python should silently
>>>> impose a policy in that case, especially given that the programmer may
>>>> have experience with any of the above treatments in other contexts.
>>> None is special in Python and has always (and intentionally) sorted
>>> before any other object. In data processing and elsewhere in Python
>>> programming, it's used to signal: no value available.
>> This is the first I've ever heard of that sorting behaviour being an
>> intentional feature, rather than just an artefact of Python 2 allowing
>> arbitrarily ordered comparisons between different types. Can you point me
>> to the relevant entry in the Python 2 language reference?
> This is not documented anywhere in the language spec, AFAIK.


"The operators <, >, ==, >=, <=, and != compare the values of two 
objects. The objects need not have the same type. If both are numbers, 
they are converted to a common type. Otherwise, objects of different 
types always compare unequal, and are ordered consistently but arbitrarily."


"Objects of different types, except different numeric types and 
different string types, never compare equal; such objects are ordered 
consistently but arbitrarily"

It goes on to note the exception for complex numbers, but none for None. 
It continues

"CPython implementation detail: Objects of different types except 
numbers are ordered by their type names;"

Again, there is no exception noted for None, although, since the type 
name of None is 'NoneType', its behavior does not match that doc note.

I believe that CPython implementation detail was some of the arbitrary 
orderings changed in CPython between 1.3 and 2.7. I do not know whether 
other implementations all mimicked CPython or not, but the reference 
manual does not require that.

> It is documented in the code (Python 2.7; Object/object.c):
> default_3way_compare(PyObject *v, PyObject *w)

This is the 'final fallback' for comparisons, not the first thing tried.

> ...
>      /* None is smaller than anything */

Reading CPython C code is not supposed to be a requirement for 
programming in Python. If that comment were true, I would regard it as 
only documenting a version of CPython, not the language standard. But it 
is not even true in 2.7.6. But it is not even true.

 >>> class Bottom(object):  # get same results below without 'object'
	def __lt__(self, other):
		return True
# the following two results are consistent and
# contradict the claim that 'None is smaller than anything'
 >>> Bottom() < None
 >>> cmp(Bottom(), None)

# the following two results are not consistent with the
# definition of cmp, so 1 of the 2 is buggy
 >>> None < Bottom()
 >>> cmp(None, Bottom())

It appears that 'anything' needs to be qualified as something like 
'anything that does not itself handle comparison with None or is not 
given a chance to do so in a particular comparison expression'.

>      if (v == Py_None)
>          return -1;
>      if (w == Py_None)
>          return 1;

> Note that it's not important whether None is smaller or greater
> than any other object. The important aspect is that it's sorting
> order is consistent and doesn't raise a TypeError when doing an
> ordered comparison with other objects.

I can agree that it might have been nice if None had been defined as a 
universal bottom object, but it has not been so defined and it did not 
act as such. To make it (or anything else) a true bottom object would 
require a change in the way comparisons are evaluated. Comparisons with 
'bottom' would have to be detected and special-cased at the beginning of 
the code, not at the end as a fallback.

Terry Jan Reedy

More information about the Python-Dev mailing list