How can I fix a TypeError that says an operator (<, <=, >, >=) is not supported between x and y?

Question:

I often see error messages that look like any of:

TypeError: '<' not supported between instances of 'str' and 'int'

The message can vary quite a bit, and I guess that it has many causes; so rather than ask again every time for every little situation, I want to know: what approaches or techniques can I use to find the problem, when I see this error message? (I have already read I'm getting a TypeError. How do I fix it?, but I’m looking for advice specific to the individual pattern of error messages I have identified.)

So far, I have figured out that:

  • the error will show some kind of operator (most commonly <; sometimes >, <=, >= or +) is "not supported between instances of", and then two type names (could be any types, but usually they are not the same).

  • The highlighted code will almost always have that operator in it somewhere, but the version with < can also show up if I am trying to sort something. (Why?)

Asked By: Karl Knechtel

||

Answers:

Overview

As with any other TypeError, the main steps of the debugging task are:

  • Figure out what operation is raising the exception, what the inputs are, and what their types are
  • Understand why these types and operation cause a problem together, and determine which is wrong
  • If the input is wrong, work backwards to figure out where it comes from

The "working backwards" part is the same for all exceptions, but here are some specific hints for the first two steps.

Identifying the operation and inputs

This error occurs with the relational operators (or comparisons) <, >, <=, >=. It won’t happen with == or != (unless someone specifically defines those operators for a user-defined class such that they do), because there is a fallback comparison based on object identity.

Bitwise, arithmetic and shifting operators give different error messages. (The boolean logical operators and and or do not normally cause a problem because of their logic is supported by every type by default, just like with == and !=. As for xor, that doesn’t exist.)

As usual, start by looking at the last line of code mentioned in the error message. Go to the corresponding file and examine that line of code. (If the code is line-wrapped, it might not all be shown in the error message.)

Try to find an operator that matches the one in the error message, and double-check what the operands will be i.e. the things on the left-hand and right-hand side of the error. Double-check operator precedence to make sure of what expression will feed into the left-hand and right-hand sides of the operator. If the line is complex, try rewriting it to do the work in multiple steps. (If this accidentally fixes the problem, consider not trying to put it back!)

Sometimes the problem will be obvious at this point (for example, maybe the wrong variable was used due to a typo). Otherwise, use a debugger (ideally) or print traces to verify these values, and their types, at the time that the error occurs. The same line of code could run successfully many other times before the error occurs, so figuring out the problem with print can be difficult. Consider using temporary exception handling, along with breaking up the expression:

# result = complex_expression_a() < complex_expression_b()
try:
    lhs, rhs = complex_expression_a(), complex_expression_b()
    result = lhs < rhs
except TypeError:
    print(f'comparison failed between `{lhs}` of type `{type(lhs)}` and `{rhs}` of type `{type(rhs)}`')
    raise # so the program still stops and shows the error

Special case: sorting

As noted in the question, trying to sort a list using its .sort method, or to sort a sequence of values using the built-in sorted function (this is basically equivalent to creating a new list from the values, .sorting it and returning it), can cause TypeError: '<' not supported between instances of... – naming the types of two of the values that are in the input. This happens because general-purpose sorting involves comparing the values being sorted, and the built-in sort does this using <. (In Python 2.x, it was possible to specify a custom comparison function, but now custom sort orders are done using a "key" function that transforms the values into something that sorts in the desired way.)

Therefore, if the line of code contains one of these calls, the natural explanation is that the values being sorted are of incompatible types (typically, mixed types). Rather than looking for left- and right-hand side of an expression, we look at a single sequence of inputs. One useful technique here is to use set to find out all the types of these values (looking at individual values will probably not be as insightful):

try:
    my_data.sort()
except TypeError:
    print(f'sorting failed. Found these types: {set(type(d) for d in my_data)}')
    raise

See also LabelEncoder: TypeError: '>' not supported between instances of 'float' and 'str' for a Pandas-specific variant of this problem.

If all the input values are the same type, it could still be that the type does not support comparison (for example, a list of all None cannot be sorted, despite that it’s obvious that the result should just be the same list). A special note here: if the input was created using a list comprehension, then the values will normally be of the same type, but that type could be invalid. Carefully check the logic for the comprehension. If it results in a function, or in None, see the corresponding sections below.

Historical note

This kind of error is specific to Python 3. In 2.x, objects could be compared regardless of mismatched types, following rather complex rules; and certain things of the same type (such as dicts) could be compared that are no longer considered comparable in 3.x.

This meant that data could always be sorted without causing a cryptic error; but the resulting order could be hard to understand, and this permissive behaviour often caused many more problems than it solved.

Understanding the incompatibility

For comparisons, it’s very likely that the problem is with either or both of the inputs, rather than the operator; but double-check the intended logic anyway.

For simple cases of sorting an input sequence, similarly, the problem is almost certainly with the input values. However, when sorting using a key function (e.g. mylist.sort(key=lambda x: ...), that function could also cause the problem. Double-check the logic: given the expected type for the input values, what type of thing will be returned? Does it make sense to compare two things of that type? If an existing function is used, test the function with some sample values. If a lambda is used, convert it to a function first and test that.

If the list is supposed to contain instances of a user-defined class, make sure that the class instances are created properly. Consider for example:

class Example:
    def __init__(self):
        self.attribute = None

mylist = [Example(), Example()]
mylist.sort(key=lambda e: e.attribute)

The key function was supposed to make it possible to sort the instances according to their attribute value, but those values were set wrongly to None – thus we still get an error, because the Nones returned from the key function are not comparable.

Comparing NoneType

NoneType is the type of the special None value, so this means that either of the operands (or one or more of the elements of the input) is None.

Check:

  • If the value is supposed to be provided by a user-defined function, make sure that the value is returned rather than being displayed using print and that the return value is used properly. Make sure that the function explicitly returns a non-None value without reaching the end, in every case. If the function uses recursion, make sure that it doesn’t improperly ignore a value returned from the recursive call (i.e., unless there is a good reason).
  • If the value is supposed to come from a built-in method or a library function, make sure that it actually returns the value, rather than modifying the input as a side effect. This commonly happens for example with many list methods, random.shuffle, and print (especially a print call left over from a previous debugging attempt). Many other things can return None in some circumstances rather than reporting an error. When in doubt, read the documentation.

Comparing functions (or methods)

This almost always means that the function was not called when it should have been. Keep in mind that the parentheses are necessary for a call even if there are no arguments.

For example, if we have

import random

if random.random < 0.5:
    print('heads')
else:
    print('tails')

This will fail because the random function was not called – the code should say if random.random() < 0.5: instead.

Comparing strings and numbers

If one side of the comparison is a str and the other side is int or float, this typically suggests that the str should have been converted earlier on, as in this example. This especially happens when the string comes from user input.

Comparing user-defined types

By default, only == and != comparisons are possible with user-defined types. The others need to be implemented, using the special methods __lt__ (<), __le__ (<=), __gt__ (>) and/or __ge__ (>=). Python 3.x can make some inferences here automatically, but not many:

>>> class Example:
...     def __init__(self, value):
...         self._value = value
...     def __gt__(self, other):
...         if isinstance(other, Example):
...             return self._value > other._value
...         return self._value > other # for non-Examples
... 
>>> Example(1) > Example(2) # our Example class supports `>` comparison with other Examples
False
>>> Example(1) > 2 # as well as non-Examples.
False
>>> Example(1) < Example(2) # `<` is inferred by swapping the arguments, for two Examples...
True
>>> Example(1) < 2 # but not for other types
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Example' and 'int'
>>> Example(1) >= Example(2) # and `>=` does not work, even though `>` and `==` do
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>=' not supported between instances of 'Example' and 'Example'

In 3.2 and up, this can be worked around using the total_ordering decorator from the standard library functools module:

>>> from functools import total_ordering
>>> @total_ordering
... class Example:
...     # the rest of the class as before
>>> # Now all the examples work and do the right thing.
Answered By: Karl Knechtel