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?
This is a great example of why the
__dunder__
methods should not be used directly as they are quite often not appropriate replacements for their equivalent operators; you should use the==
operator instead for equality comparisons, or in this special case, when checking forNone
, useis
(skip to the bottom of the answer for more information).You've done
Which returns
NotImplemented
since the types being compared are different. Consider another example where two objects with different types are being compared in this fashion, such as1
and'a'
. Doing(1).__eq__('a')
is also not correct, and will returnNotImplemented
. The right way to compare these two values for equality would beWhat happens here is
(1).__eq__('a')
is tried, which returnsNotImplemented
. This indicates that the operation is not supported, so'a'.__eq__(1)
is called, which also returns the sameNotImplemented
. So,False
is returned.Here's a nice little MCVE using some custom classes to illustrate how this happens:
Of course, that doesn't explain why the operation returns true. This is because
NotImplemented
is actually a truthy value:Same as,
For more information on what values are considered truthy and falsy, see the docs section on Truth Value Testing, as well as this answer. It is worth noting here that
NotImplemented
is truthy, but it would have been a different story had the class defined a__bool__
or__len__
method that returnedFalse
or0
respectively.If you want the functional equivalent of the
==
operator, useoperator.eq
:However, as mentioned earlier, for this specific scenario, where you are checking for
None
, useis
:The functional equivalent of this is using
operator.is_
:None
is a special object, and only 1 version exists in memory at any point of time. IOW, it is the sole singleton of theNoneType
class (but the same object may have any number of references). The PEP8 guidelines make this explicit:In summary, for singletons like
None
, a reference check withis
is more appropriate, although both==
andis
will work just fine.The result you are seeing is caused by that fact that
evaluates to
NotImplemented
, andNotImplemented
's truth value is documented to beTrue
:https://docs.python.org/3/library/constants.html
If you call the
__eq()__
method manually rather than just using==
, you need to be prepared to deal with the possibility it may returnNotImplemented
and that its truth value is true.As you already figured
None.__eq__("a")
evaluates toNotImplemented
however if you try something likethe result is
this mean that the truth value of
NotImplemented
true
Therefor the outcome of the question is obvious:
None.__eq__(something)
yieldsNotImplemented
And
bool(NotImplemented)
evaluates to TrueSo
if None.__eq__("a")
is always TrueWhy?
It returns a
NotImplemented
, yeah:But if you look at this:
NotImplemented
is actually a truthy value, so that's why it returnsb
, anything that isTrue
will pass, anything that isFalse
wouldn't.How to solve it?
You have to check if it is
True
, so be more suspicious, as you see:So you would do:
And as you see, it wouldn't return anything.