1

In Python 2, it was possible to compare objects of different types such as int to str by having an implicit comparison of the text string of types (that is, in lexicographic order, string 'int' is less than string 'str' and string 'list' is less than string 'tuple').

Hence, in Python 2, 5 < 'hello' returns True. One can read more about why this was allowed in answer to Why is ''>0 True in Python?.

In Python 3, this raises builtins.TypeError: unorderable types: int() < str() exception.

This web page says

The strict approach to comparing in Python 3 makes it generally impossible to compare different types of objects.

Does it mean there are some built-in types or special cases where it would be possible for any built-in types to be compared without causing TypeError? I am not talking about custom types where the necessary dunder methods are implemented to properly support comparison.

Alex Tereshenkov
  • 3,340
  • 8
  • 36
  • 61
  • Sure, you can compare `int` to `float` for a start. You can also compare them with `bool`. You can also compare `set` to `frozenset`. – cglacet Apr 15 '19 at 18:14
  • 1
    There's more to the Python 2 behavior than just string-comparing the type names. For example, `5 < ArithmeticError()` even though `'int' > 'ArithmeticError'`. – user2357112 Apr 15 '19 at 18:54

2 Answers2

1

All these are valid statements (and they all evaluate to True):

0 < True
0 < 1.
0. < True
{0} < frozenset((0, 1))

The only thing that may look odd here is that 0. == False and 1. == True.

On the other hand, you can still reproduce what python 2 does by casting your value to an str before comparing it (this also evaluate to True):

str(5) < 'hello'

If you really need this behaviour you can always have a function that will cast/compare. That way you'll guarantee that objects of different types will always compare the same way, which seems to be the only constraint in python 2.

def lt(a, b):
    return str(a) < str(b)

Or maybe even better, you can cast only when needed:

def lt(a, b):
  try: 
    return a < b
  except TypeError: 
    return str(a) < str(b)

On the other hand, as suggested in the comments, in CPython's implementation, it seems like comparison is done in the following way:

def lt(a, b):
  try: 
    return a < b
  except TypeError: 
    if a is None:
      return True
    if b is None: 
      return False
    if isinstance(a, numbers.Number):
      return True
    if isinstance(b, numbers.Number):
      return False
    return str(type(a)) < str(type(b))
cglacet
  • 8,873
  • 4
  • 45
  • 60
  • OK, this is useful. Comparison of `int` and `bool` discussed here https://stackoverflow.com/questions/2764017/is-false-0-and-true-1-in-python-an-implementation-detail-or-is-it-guarante – Alex Tereshenkov Apr 15 '19 at 18:29
  • `bool` is a subclass of `int`, so it's not really odd that you can compare `bool`s to other numbers. – chepner Apr 15 '19 at 18:38
  • Maybe that's because I'm used to 0/1 acting as bool, maybe comparing `int` to `bool` is also odd and I just got over it. – cglacet Apr 15 '19 at 18:41
  • 1
    The Python 2 behavior is not equivalent to `str(5) < 'hello'`. The Python 2 behavior is a bizarre, implementation-dependent pile of quirks, but for the specific case of `5 < 'hello'` on CPython, `5` is considered smaller because it's a number and `'hello'` isn't. – user2357112 Apr 15 '19 at 18:42
  • 1
    You may have misinterpreted the part where the question asserts that the values are compared by *type* name, not by stringifying the values. Type name comparison is a part of the weird pile of quirks, though not the whole story. – user2357112 Apr 15 '19 at 18:45
  • Aren't number always "smaller" than strings with this solution? Or maybe you mean that other types will not be sorted similarly as in python 2? – cglacet Apr 15 '19 at 18:47
  • 1
    With your code, `5` compares greater than `'4'`, contrary to how `5 < '4'` in actual CPython 2. – user2357112 Apr 15 '19 at 18:53
  • Ok, I'll try to update my answer with something that look a bit more like what python 2 does then. – cglacet Apr 15 '19 at 20:34
  • It looks like what I implemented is also valid as the only constraint in python 2 seems to be consistency. – cglacet Apr 15 '19 at 20:41
  • One more problem with None, None. py3: lt(None, None) =>True; py2: lt(None, None) => False; py2: (None < None) => None. – Ilia w495 Nikitin Jan 15 '20 at 16:30
1

I had already looked this up on the web before, and it appears they are indeed unsortable in Python 3, apart from the few special cases mentionned above.

The change usually manifests itself in sorting lists: in Python 3, lists with items of different types are generally not sortable. If you need to sort heterogeneous lists, or compare different types of objects, implement a key function to fully describe how disparate types should be ordered.
Source

I don't know why, but some found ways to reproduce the behavior of Python 2 using Python 3.

Maybe you should take a look at this or that. This question also highlighted the change in 2011:

Found it: Buried in PEP 3100: "Comparisons other than == and != between disparate types will raise an exception unless explicitly supported by the type"

Toby1253
  • 82
  • 3