Can anyone explain this behaviour in Python (2.7 and 3)
>>> a = "Monday" and "tuesday"
>>> a
'tuesday' # I expected this to be True
>>> a == True
False # I expected this to be True
>>> a is True
False # I expected this to be True
>>> a = "Monday" or "tuesday"
>>> a
'Monday' # I expected this to be True
>>> a == True
False # I expected this to be True
>>> a is True
False # I expected this to be True
I would expect that because I am using logic operators and and or, the statements would be evaluated as a = bool("Monday") and bool("tuesday").
So what is happening here?
As explained here using and / or on strings will yield the following result:
a or b returns a if a is True, else returns b.
a and b returns b if a is True, else returns a.
This behavior is called Short-circuit_evaluation and it applies for both and, or as can be seen here.
This explains why a == 'tuesday' in the 1st case and 'Monday' in the 2nd.
As for checking a == True, a is True, using logical operators on strings yields a specific result (as explained in above), and it is not the same as bool("some_string").
Related
I've just started learning Python and i was trying this
[] == False #False
but :
bool([]) #False
from what i got values like [],0 .. are False what did i missed exactly and thanks!
The operator == is very literal. If the 2 things you are comparing are not exactly the same (this includes types, like "2" == 2 is False) then the result will always be False. So the boolean False is not literally the same thing as an empty list [] which is why [] == False is False.
An empty list is just treated as "False" when converted to a boolean, which you did with bool([]). So the output of bool([]) is False which is literally the same as False. Thus bool([]) == False is True.
[] != False, but bool([]) == bool(False).
This may be simply idiotic, but for me it's a bit confusing:
In [697]: l=[]
In [698]: bool(l)
Out[698]: False
In [699]: l == True
Out[699]: False
In [700]: l == False
Out[700]: False
In [701]: False == False
Out[701]: True
Why does l==False return False while False == False returns True?
You are checking it against the literal value of the boolean False. The same as 'A' == False will not be true.
If you cast it, you'll see the difference:
>>> l = []
>>> l is True
False
>>> l is False
False
>>> l == True
False
>>> l == False
False
>>> bool(l) == False
True
The reason False == False is true is because you are comparing the same objects. It is the same as 2 == 2 or 'A' == 'A'.
The difficulty comes when you see things like if l: and this check never passes. That is because you are checking against the truth value of the item. By convention, all these items will fail a boolean check - that is, their boolean value will be False:
None
False (obviously)
Any empty sequence: '', [], ()
Any "zero" value: 0, 0.0, etc.
Any empty collection: {} (an empty dict)
Anything whose len() returns a 0
These are called "falsey" values. Everything else is "true". Which can lead to some strange things like:
>>> def foo():
... pass
...
>>> bool(foo)
True
It is also good to note here that methods that don't return an explicit value, always have None as their return type, which leads to this:
>>> def bar():
... x = 1+1
...
>>> bool(bar)
True
>>> bool(bar())
False
An empty list is not the same as False, but False equals False because it's the same object. bool(l) returns False because an empty list is "falsy".
In short, == is not bool() == bool().
For example, [1, 2] == [1, 2, 3] is False, even if the two are "truly".
It is because the empty list is not False, it is just "falsy" when converted to a bool, or when evaluated by the an if or while condition (which both evaluate the bool conversion of their condition). See the documentation on Truth Value Testing for more detail.
Let's say I have a list with different values, like this:
[1,2,3,'b', None, False, True, 7.0]
I want to iterate over it and check that every element is not in list of some forbidden values. For example, this list is [0,0.0].
When I check if False in [0,0.0] I get True. I understand that python casts False to 0 here - but how I can avoid it and make this check right - that False value is not in [0,0.0]?
To tell the difference between False and 0 you may use is to compare them. False is a singleton value and always refers to the same object. To compare all the items in a list to make sure they are not False, try:
all(x is not False for x in a_list)
BTW, Python doesn't cast anything here: Booleans are a subclass of integers, and False is literally equal to 0, no conversion required.
You would want to use is instead of == when comparing.
y = 0
print y == False # True
print y is False # False
x = False
print x == False # True
print x is False # True
Found a weird corner case on differentiating between 0 and False today. If the initial list contains the numpy version of False (numpy.bool_(False)), the is comparisons don't work, because numpy.bool_(False) is not False.
These arise all the time in comparisons that use numpy types. For example:
>>> type(numpy.array(50)<0)
<class 'numpy.bool_'>
The easiest way would be to compare using the numpy.bool_ type: (np.array(50)<0) is (np.False_). But doing that requires a numpy dependency. The solution I came up with was to do a string comparison (working as of numpy 1.18.1):
str(numpy.bool_(False)) == str(False)
So when dealing with a list, a la #kindall it would be:
all(str(x) != str(False) for x in a_list)
Note that this test also has a problem with the string 'False'. To avoid that, you could exclude against cases where the string representation was equivalent to itself (this also dodges a numpy string array). Here's some test outputs:
>>> foo = False
>>> str(foo) != foo and str(foo) == str(False)
True
>>> foo = numpy.bool_(False)
>>> str(foo) != foo and str(foo) == str(False)
True
>>> foo = 0
>>> str(foo) != foo and str(foo) == str(False)
False
>>> foo = 'False'
>>> str(foo) != foo and str(foo) == str(False)
False
>>> foo = numpy.array('False')
>>> str(foo) != foo and str(foo) == str(False)
array(False)
I am not really an expert programmer, so there may be some limitations I've still missed, or a big reason not to do this, but it allowed me to differentiate 0 and False without needing to resort to a numpy dependency.
In my program encountered with this:
>>> True and True and (3 or True)
3
>>> True and True and ('asd' or True)
'asd'
while I expected to get some boolean value depending on the result in brackets. So if I try comparisons like these (0 or True) or ('' or True) python will return True, which is clear because 0 and '' equivalent to False in comparisons.
Why doesn't python return boolean value by converting 3 and 'asd' into True?
From https://docs.python.org/3/library/stdtypes.html:
Important exception: the Boolean operations or and and always return
one of their operands
The behavior can be most easily seen with:
>>> 3 and True
True
>>> True and 3
3
If you need to eliminate this behavior, wrap it in a bool:
>>> bool(True and 3)
True
See this question
As Reut Sharabani, answered, this behavior allows useful things like:
>>> my_list = []
>>> print (my_list or "no values")
This may be simply idiotic, but for me it's a bit confusing:
In [697]: l=[]
In [698]: bool(l)
Out[698]: False
In [699]: l == True
Out[699]: False
In [700]: l == False
Out[700]: False
In [701]: False == False
Out[701]: True
Why does l==False return False while False == False returns True?
You are checking it against the literal value of the boolean False. The same as 'A' == False will not be true.
If you cast it, you'll see the difference:
>>> l = []
>>> l is True
False
>>> l is False
False
>>> l == True
False
>>> l == False
False
>>> bool(l) == False
True
The reason False == False is true is because you are comparing the same objects. It is the same as 2 == 2 or 'A' == 'A'.
The difficulty comes when you see things like if l: and this check never passes. That is because you are checking against the truth value of the item. By convention, all these items will fail a boolean check - that is, their boolean value will be False:
None
False (obviously)
Any empty sequence: '', [], ()
Any "zero" value: 0, 0.0, etc.
Any empty collection: {} (an empty dict)
Anything whose len() returns a 0
These are called "falsey" values. Everything else is "true". Which can lead to some strange things like:
>>> def foo():
... pass
...
>>> bool(foo)
True
It is also good to note here that methods that don't return an explicit value, always have None as their return type, which leads to this:
>>> def bar():
... x = 1+1
...
>>> bool(bar)
True
>>> bool(bar())
False
An empty list is not the same as False, but False equals False because it's the same object. bool(l) returns False because an empty list is "falsy".
In short, == is not bool() == bool().
For example, [1, 2] == [1, 2, 3] is False, even if the two are "truly".
It is because the empty list is not False, it is just "falsy" when converted to a bool, or when evaluated by the an if or while condition (which both evaluate the bool conversion of their condition). See the documentation on Truth Value Testing for more detail.