1

For some reason in Python 2.7 expressions of the form tuple > list return True, but tuple < list and tuple == list return False. Why is that?

This observation is not original to me by any means.

Nate
  • 12,499
  • 5
  • 45
  • 60
Alexei Averchenko
  • 1,706
  • 1
  • 16
  • 29
  • See [Why is ''>0 True in Python?](http://stackoverflow.com/questions/2384078/why-is-0-true-in-python) and [Why does 4 < '3' return True in Python 2?](http://stackoverflow.com/questions/7969552/why-does-4-3-return-true-in-python-2) – chown Nov 02 '11 at 19:01

4 Answers4

6

tuple and list are not the same type. Python does something kind of surprising when comparing values that are not of the same type, and which don't define comparators that work across these types. it compares the dictionary order of the names of the classes:

>>> class Coffee(object):
...     pass
... 
>>> class Tea(object):
...     pass
... 
>>> c = Coffee()
>>> t = Tea()
>>> c > t
False
>>> c == t
False
>>> c < t
True
>>> 

Thankfully, in python 3, this goes away, comparing such types raises an exception.

SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
  • "`tuple` and `lis`t are not of the same type." That's wrong, they are both of type `type`. – Sven Marnach Nov 02 '11 at 19:03
  • @Sven Marnach: Perhaps my grammar was not totally clear, the two types `list` and `tuple` *have* the same type, `type(list) is type(tuple)` but they *are not* the same type, `type(list) is type and type(tuple) is type and list is not tuple` – SingleNegationElimination Dec 25 '11 at 20:35
  • `tuple` and `list` oviously are different types, but that's completely immaterial for this question. The OP asked about the strange result of `list < tuple` etc, but your answer talks about comparing values of different types all the time, even in the example code. It's definitely not a question of your grammar being unclear -- my point is that this answer misses the point (though only slightly). You explain that values of different types are ordered by the dictionary order of their type names -- how is this relevant for comparing `list` and `tuple`? – Sven Marnach Dec 26 '11 at 19:40
  • We interpret the question differently; I understood Alexei's " **expressions of the form** tuple > list" (emphasis mine) 'list' and 'tuple' words to be metasyntactic variables, stand-ins for values that are of those types, rather than the type values of the built in `list` or `tuple`. – SingleNegationElimination Dec 27 '11 at 03:13
  • You are right -- I did not think of your way to read the question. This makes sense, thanks for the clarification – Sven Marnach Dec 28 '11 at 14:02
5

From the doc:

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.

Nate
  • 12,499
  • 5
  • 45
  • 60
  • So basically a tuple is greater than a list because it starts with letter 't', isn't it? :) – Alexei Averchenko Nov 02 '11 at 18:51
  • It says "arbitrarily". Their ordering is simply an artifact of the implementation, and could be different using different interpreters. – Nate Nov 02 '11 at 18:52
1

Because python objects of different types are compared arbitrarily (arbitrarily means "alphabetically by their type name" at least in python 2.7). Therefore, a tuple will always be > then a list.

The Python (2.7) Language Reference - 5.9. Comparisons:

Most other objects of built-in types compare unequal unless they are the same object; the choice whether one object is considered smaller or larger than another one is made arbitrarily but consistently within one execution of a program.

The rules for comparing objects of different types should not be relied upon; they may change in a future version of the language.

This is the c function for comparing python objects (from the Python 2.7 source):

default_3way_compare(PyObject *v, PyObject *w)
{
    int c;
    const char *vname, *wname;

    if (v->ob_type == w->ob_type) {
        /* When comparing these pointers, they must be cast to
         * integer types (i.e. Py_uintptr_t, our spelling of C9X's
         * uintptr_t).  ANSI specifies that pointer compares other
         * than == and != to non-related structures are undefined.
         */
        Py_uintptr_t vv = (Py_uintptr_t)v;
        Py_uintptr_t ww = (Py_uintptr_t)w;
        return (vv < ww) ? -1 : (vv > ww) ? 1 : 0;
    }

    /* None is smaller than anything */
    if (v == Py_None)
        return -1;
    if (w == Py_None)
        return 1;

    /* different type: compare type names; numbers are smaller */
    if (PyNumber_Check(v))
        vname = "";
    else
        vname = v->ob_type->tp_name;
    if (PyNumber_Check(w))
        wname = "";
    else
        wname = w->ob_type->tp_name;
    c = strcmp(vname, wname);
    if (c < 0)
        return -1;
    if (c > 0)
        return 1;
    /* Same type name, or (more likely) incomparable numeric types */
    return ((Py_uintptr_t)(v->ob_type) < (
        Py_uintptr_t)(w->ob_type)) ? -1 : 1;
}

The main part to look at is the lines (towards the end):

else
    wname = w->ob_type->tp_name;
c = strcmp(vname, wname);
chown
  • 51,908
  • 16
  • 134
  • 170
0

Arbitrary implementation choice. It's a TypeError in Python 3.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224