[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.

http://docs.python.org/2/reference/expressions.html#not-in

"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."

http://docs.python.org/2/library/stdtypes.html#comparisons

"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
True
 >>> cmp(Bottom(), None)
-1

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

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