Why does `if None.__eq__("a")` seem to evaluate to True (but not quite)?
python __eq__ not called
__eq__ self value
__eq__ and __hash__
what is def __eq__
If you execute the following statement in Python 3.7, it will (from my testing) print
if None.__eq__("a"): print("b")
None.__eq__("a") evaluates to
"a".__eq__("a") evaluates to
"b".__eq__("a") evaluates to
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
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 for
is (skip to the bottom of the answer for more information).
None.__eq__('a') # NotImplemented
NotImplemented since the types being compared are different. Consider another example where two objects with different types are being compared in this fashion, such as
(1).__eq__('a') is also not correct, and will return
NotImplemented. The right way to compare these two values for equality would be
1 == 'a' # False
What happens here is
(1).__eq__('a')is tried, which returns
NotImplemented. This indicates that the operation is not supported, so
'a'.__eq__(1)is called, which also returns the same
- The objects are treated as if they are not the same, and
Here's a nice little MCVE using some custom classes to illustrate how this happens:
class A: def __eq__(self, other): print('A.__eq__') return NotImplemented class B: def __eq__(self, other): print('B.__eq__') return NotImplemented class C: def __eq__(self, other): print('C.__eq__') return True a = A() b = B() c = C() print(a == b) # A.__eq__ # B.__eq__ # False print(a == c) # A.__eq__ # C.__eq__ # True print(c == a) # C.__eq__ # True
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
bool(NotImplemented) # True
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
__len__ method that returned
If you want the functional equivalent of the
== operator, use
import operator operator.eq(1, 'a') # False
However, as mentioned earlier, for this specific scenario, where you are checking for
var = 'a' var is None # False var2 = None var2 is None # True
The functional equivalent of this is using
operator.is_(var2, None) # True
None is a special object, and only 1 version exists in memory at any point of time. IOW, it is the sole singleton of the
NoneType class (but the same object may have any number of references). The PEP8 guidelines make this explicit:
Comparisons to singletons like
Noneshould always be done with
is not, never the equality operators.
In summary, for singletons like
None, a reference check with
is is more appropriate, although both
is will work just fine.
Modifying the __add__ method of a Python Class, method to return a Day instance with the total number of visits and contacts: class Day(object): 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.
The result you are seeing is caused by that fact that
None.__eq__("a") # evaluates to NotImplemented
NotImplemented's truth value is documented to be
Special value which should be returned by the binary special methods (e.g.
__rsub__(), etc.) to indicate that the operation is not implemented with respect to the other type; may be returned by the in-place binary special methods (e.g.
__iand__(), etc.) for the same purpose. Its truth value is true.
If you call the
__eq()__ method manually rather than just using
==, you need to be prepared to deal with the possibility it may return
NotImplemented and that its truth value is true.
What is the purpose of checking self.__class__ ? - python, ). The double underscores indicate that these are magic methods and shouldn't be called directly by the programmer, they are normally called by the interpreter itself. 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 for None, use is (skip to the bottom of the answer for more information).
As you already figured
None.__eq__("a") evaluates to
NotImplemented however if you try something like
if NotImplemented: print("Yes") else: print("No")
the result is
this mean that the truth value of
Therefor the outcome of the question is obvious:
bool(NotImplemented) evaluates to True
if None.__eq__("a") is always True
Magic Methods, A dictionary or other mapping object used to store an object's (writable) attributes. Why does `if None.__eq__("a")` evaluate to True? Did any machines alternate between two video memory banks? Man page for file permission numbers
It returns a
>>> None.__eq__('a') NotImplemented >>>
But if you look at this:
>>> bool(NotImplemented) True >>>
NotImplemented is actually a truthy value, so that's why it returns
b, anything that is
True will pass, anything that is
How to solve it?
You have to check if it is
True, so be more suspicious, as you see:
>>> NotImplemented == True False >>>
So you would do:
>>> if None.__eq__('a') == True: print('b') >>>
And as you see, it wouldn't return anything.
What does built-in class attribute __dict__ do in Python?, B = B mocker.result(False) mocker.count(0, None) A.begins mocker.result(5) mocker.count(0, None) B = mocker.mock() A.__eq__(MATCH(lambdax:x is B)) None. __eq__ ('a') # NotImplemented 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 as 1 and 'a' .
Python Testing: Beginner's Guide, This is an example of testing overriden __eq__ and __ne__ methods. class Sandwich(object): def __init__(self, meat = None, bread = None): """ @meat Why does Bq M10 tablet file manager make duplicates when renaming? Using the Bq M10 tablet file manager to rename files located on the microSD card, a duplicate un-renamed file is copied onto the device itself, creating a duplicate and cluttering.
Testing __eq__ & __ne__, If none of the equality methods (__cmp__, __eq__, __ne__) is defined for a class, object identity is used for the equality comparison. __hash__(self) Called by The surge of passengers trying to change or cancel plane tickets as travel grinds to a halt during the coronavirus crisis is overwhelming airline and online travel agency reservation centers
Jython Essentials: Rapid Scripting in Java, If none of the operators tried returns true, the classic comparison fallbacks are other): def __ge__(self, other): def __eq__(self, other): def __ne__(self, Why does `if None.__eq__("a")` evaluate to True? What does it mean when my new HDD reports errors at a time that shouldn't exist? Would a compass with unmagnetized needle work?
- most visually clear answer - v worthwhile addition - thank you
- :) "worthwhile addition" doesn’t quite capture what I was trying to say (as you obv see) - maybe "belated excellence" is what I wanted - cheers
- @scharfmn yes? I'm curious to know what you think this answer adds that hasn't already been covered before.
- somehow the visual/repl things here add clarity - full demo
- @scharfmn ... Which the accepted answer also has albeit the prompts have been removed. Did you upvote solely because the terminal prompts have been lazily left in?