Why does `if None.__eq__(“a”)` evaluate to True?

  • A+
Category:Languages

If you execute the following statement in Python 3.7, it will (from my testing) print b:

if None.__eq__("a"):     print("b") 

However, None.__eq__("a") evaluates to NotImplemented.

Naturally, "a".__eq__("a") evaluates to True, and "b".__eq__("a") evaluates to False.

I initially discovered this when testing the return value of a function, but didn't return anything in the second case -- so, the function returned None.

What's going on here?

 


The __dunder__ methods are not actually meant to be used directly like this, use the == operator. You've done

None.__eq__('a') # NotImplemented 

Which returns NotImplemented since the types being compared are different. Try comparing any two objects with different types like this (ex (1).__eq__('a')). The right way to do this would be

None == 'a' # False 

What happens here is

  1. First, None.__eq__('a') is tried, which returns NotImplemented. This indicates that the operation is not supported, so
  2. 'a'.__eq__(None) is called, which also returns the same NotImplemented. So,
  3. The objects are treated as if they are not the same, and False is returned.

Of course, that doesn't explain why the operation returns true. This is because NotImplemented is actually a truthy value:

bool(None.__eq__("a")) # True 

Same as,

bool(NotImplemented) # True 

For more information on what values are considered truthy and falsey, see the docs section on Truth Value Testing, as well as this answer.


If you want the functional equivalent of the == operator, use operator.eq:

import operator operator.eq(None, 'a') # False 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: