23

Why does 4 < '3' return True in Python 2?

Is it because when I place single quotes around a number Python sees it as a string and strings are bigger than numbers?

agf
  • 171,228
  • 44
  • 289
  • 238
user1005318
  • 233
  • 2
  • 4

3 Answers3

31

Yes, any number will be less than any string (including the empty string) in Python 2.

In Python 3, you can't make arbitrary comparisons. You'll get a TypeError.


From the link in eryksun's comment:

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);

So at least in recent versions of CPython 2.x, type names are compared, with an empty string used instead of the type name for any numeric type.

agf
  • 171,228
  • 44
  • 289
  • 238
  • 5
    See [default_3way_compare](http://hg.python.org/cpython/file/0a5eb57d5876/Objects/object.c#l778) in object.c. – Eryk Sun Nov 01 '11 at 17:14
  • 1
    @chown eryksun's got it (again, he always has great comments). It's by type name, with an empty string used instead of the type name for any numeric type. – agf Nov 01 '11 at 17:58
  • @agf Nice, the c code doesn't lie and it seems my answer was completly wrong. Good stuff :). 1 more up vote for you sir agf! – chown Nov 01 '11 at 19:12
  • see http://docs.python.org/tutorial/datastructures.html#id1 also the footnote : The rules for comparing objects of different types should not be relied upon; they may change in a future version of the language.docs.python.org trust able ? – bilash.saha Nov 01 '11 at 19:24
  • @bilash.saha Those are the Python 2.7 docs. As 2.7 is the last Python 2 version, it won't be changing any more, though it could be different in other implementations of Python. We already know how it changed in Python 3. My description of _why_ `4 < '3'` is still correct, in any case, while yours is still wrong for numeric types. – agf Nov 01 '11 at 19:31
7

From Python v2.7.2 documentation

Objects of different types except numbers are ordered by their type names; objects of the same types that don’t support proper comparison are ordered by their address.

When you order two strings or two numeric types the ordering is done in the expected way (lexicographic ordering for string, numeric ordering for integers).

When you order a string and an integer the type names are ordered. "str" is lexicographically after "int", "float", "long", "list", "bool", etc. However a tuple will order higher than a string because "tuple" > "str":

0 > 'hi'
False
[1, 2] > 'hi'
False
(1, 2) > 'hi'
True

also see comparison uses lexicographical ordering from docs.python.org

In Python 3.x the behaviour has been changed so that attempting to order an integer and a string will raise an error:

>>> '10' > 5
Traceback (most recent call last):
File "", line 1, in 
'10' > 5
TypeError: unorderable types: str() > int()
bilash.saha
  • 7,226
  • 2
  • 35
  • 40
  • `long() < list()` is `True` so it's not strictly by type name. – agf Nov 01 '11 at 16:35
  • 2
    There is a reason that is listed as a CPython implementation detail -- it's arbitrary and not to be relied on, and may only be sort-of-true or only true for some versions of Python. See my previous comment for proof -- I'm using the exact version of Python those docs supposedly describe and it's not true for certain types. – agf Nov 01 '11 at 16:42
  • @user1005318: This is a much better answer than the accepted answer. – Omnifarious Nov 01 '11 at 17:54
  • 1
    @Omnifarious Except it's not quite right, see the counterexample in my first comment. Thanks to eryksun, my answer now includes a correct explaination. – agf Nov 01 '11 at 18:01
  • see http://docs.python.org/tutorial/datastructures.html#id1 also the footnote : The rules for comparing objects of different types should not be relied upon; they may change in a future version of the language. docs.python.org trust able ? – bilash.saha Nov 01 '11 at 19:23
-1

The default comparison operation in cpython 2 is based on the memory address of the object in question. From type_richcompare() in python 2.7:

/* Compare addresses */
vv = (Py_uintptr_t)v;
ww = (Py_uintptr_t)w;
switch (op) {
case Py_LT: c = vv <  ww; break;
case Py_LE: c = vv <= ww; break;
case Py_EQ: c = vv == ww; break;
case Py_NE: c = vv != ww; break;
case Py_GT: c = vv >  ww; break;
case Py_GE: c = vv >= ww; break;
default:
    result = Py_NotImplemented;
    goto out;
}
result = c ? Py_True : Py_False;

This works really well for equality and inequality, but can be counter-intuitive for the ordering operations, so it has been changed for Python 3. Indeed, 2.7 will issue a warning for such usages when given the -3 flag.

To see the memory address of a given object, you can use the id() builtin function on it. It's only defined to return something unique for each individual object, but cpython uses the object's memory address as a convenient shortcut.

Small integers just happen to have smaller memory addresses than short strings, at least in certain python versions, probably due to the caching used by cpython to enhance performance.

eswald
  • 8,368
  • 4
  • 28
  • 28
  • 2
    This is only for two objects _of the same type_ that don't have comparisons defined, so it doesn't apply in this case. See my answer for what's going on when comparing two objects of different types. – agf Nov 01 '11 at 18:23