Python if statements and truth value testing for non compsci background - python

This aims to be a self answered question after a few hours of digging, I found this thought process may prove useful to others who also may not have come from a formal compsci background.
This all started from confusion over why one particular if statement was being entered.
>>>if (2 & 2):
... print("true")
true
Why was this if statement being entered?

My previous use of if statements were all quite straightforward, so I took it for granted and with the understanding that only a boolean True would result in the entering of an if statement. What constitutes a boolean True, was not so obvious to me after running into this if statement. This is surely obvious to those with a compsci background, but I never really dug into how if statements determined if the argument was a boolean True or not. For example, if (5 > 2) --> True just makes sense to anyone with at least an elementary-school math background, so I took it for granted. On the contrary, if (2) --> True, does not seem too obvious to a non compsci specialist.
For example
>>>2 & 2
2
>>> 2 == True
False
>>>if (2):
... print("true")
true
Why was the if statement entered despite 2 & 2 evaluating to an int value of 2? Why was the if statement entered if given the int value 2 even though 2 itself is not == True? This was the first I had seen this type of behavior and it lead me to understand from various other stack overflow questions that the if statement is not evaluating if the int value of 2 == True, but instead evaluating bool(2) == True, which is in fact True.
Again, if you come from a comp sci background, I'm sure this is all very obvious, but even the idea of bool(2) and "truth value testing" as a phrase was new to me, so adding this on to the binary logical operators caused me a fair bit of confusion at first. For example:
>>>2 & 4
0
>>>if (2 & 4):
... print("true")
>>>bin(2 & 4)
'0b0'
>>>if ('0b0'):
... print("true")
true
>>>if (0b0):
... print("true")
>>>bool(0)
False
>>>bool(0b0)
False
>>>bool(b'0')
True
>>>bool(bin(0))
True
At first, with the misunderstanding that the if statements were evaluating if the argument == True, the above examples seemed quite illogical, as I thought a binary 1 should result in True and binary 0 should result in False. I had no idea why integer values above 1, in either int or binary form should return True.
After reading through the python docs for Truth Value Testing I see a few examples of objects that are considered false. Here, and rather obviously, it makes sense why bool(0) and bool(0b0) returns False. The opposite though, for bool(b'0') and bool(bin(0)), where this seems to return True. Why is this? Because bin() returns a string representation of the number, as is b'0' (a string) not an actual binary value (like 0b0), and since these strings are not empty strings, but rather filled with the characters representing zero, it evaluates to True (See third bullet point of Truth Value Testing).
I gave a few examples of non-obvious (to me) truth evaluation tests, and why they do actually make logical sense. Hope this helps others who may be mystified by the seemingly (to me) lesser common uses of the if statement.

Related

If not... followed by function

I am stuck with this question. I am not too concerned about what each function does, but more importantly how does the IF statement work with functions. From my understanding, the IF.... or statements usually work with a condition, but for this scenario it only involves two functions without any conditions?
def disk_check_usage(disk):
du = shutil.disk_usage(disk)
free = du.free/du.total * 100
return free > 20
def check_cpu_usage():
usage = psutil.cpu_percent(1)
return usage < 75
if not disk_check_usage("/") or not check_cpu_usage():
print("ERROR!")
else:
print("Everything is OK")
I want it to give an 'Error!' message when both conditions (free > 20 and usage < 75) are not True/Satisfied.
Edit: When I run the code, 'free' = 17 which gives 'False' and Usage < 75 which gives 'True'. So my IF statement would mean 'If not False or not True:'. What does that mean and how does the system whether to run 'if' or 'else' statement?
Any help will be appreciated!
They are some operations you can do with booleans.
In particular, (not a) or (not b) is equivalent to not(a and b); at least one of the two have to be False
You just have to inverse your tow statements: if everything is ok, print ok else print error
if disk_check_usage("/") and check_cpu_usage():
print("Everything is OK")
else:
print("Error")
From my understanding, the IF.... or statements usually work with a
condition, but for this scenario it only involves two functions
without any conditions?
There's always a condition. The code you posted is no exception.
What you call a "condition" is really just a boolean expression in a specific context. If the entire expression evaluates to True, you enter the body of the if-statement. Otherwise, if it evaluates to False, you don't.
Example
The following code iterates over the characters in a string, and only prints characters which are both alphabetic and uppercase:
string = "Hell2O]WoR3(Ld"
for char in string:
if char.isalpha() and char.isupper():
print(char)
Output:
H
O
W
R
L
>>>
str.isalpha returns a boolean - True if the string (in this case a string consisting of a single character) contains only alphabetic characters, False otherwise.
str.isupper returns a boolean - True if the string (again, just a single character in this case) contains only uppercase characters, False otherwise.
I picked these two string methods to simulate the two functions you have in your code, since they also return booleans.
Let's take the first character, "H":
>>> char = "H"
>>> char.isalpha()
True
>>> char.isupper()
True
>>> char.isalpha() and char.isupper()
True
>>>
You see, the entire boolean expression char.isalpha() and char.isupper() evaluates to True. In order for the entire expression to be evaluated, both functions need to be called - you can think of their return values effectively "replacing" their respective function calls. When char is "H", after calling the functions, the expression really looks like this:
True and True
Which evaluates to True, so we enter the body of the if-statement.
In your case, you used or instead of and. What I said about boolean expressions collapsing down into a single True or False value still applies, the only difference would be in short-circuiting, which isn't that relevant to your question I think.
Example
Here's something closer to what you have. Imagine a scenario where you want to monitor the temperature and radiation of something. If either the temperature OR the radiation levels are not nominal, we trigger an alarm.
def is_temperature_nominal(temp):
return 70 <= temp <= 100
def is_radiation_nominal(rad):
return rad < 200
if not is_temperature_nominal(250) or not is_radiation_nominal(50):
print("Alarm triggered!")
else:
print("Everything is good.")
Output:
Alarm triggered!
>>>
I picked the nominal ranges arbitrarily, and I picked the arguments 250 and 50 arbitrarily as well. With these two hardcoded values, the boolean expression would look like this after calling the two functions:
not False or not True
Which is another way of saying:
True or False
With an or, only one of the operands has to be True in order for the entire expression to evaluate to True - therefore the entire expression evaluates to True, and we enter the body of the if-statement, triggering the alarm (because only one of the values needs to be not nominal in order for the alarm to trigger. If both values are not nominal it would also trigger the alarm. The only way we enter the body of the else is if both values are nominal).
From my understanding, the IF.... or statements usually work with a
condition, but for this scenario it only involves two functions
without any conditions
if (as well as while) needs an expression that evaluates to a logical (type bool) value (either True or False), or can be typecast to a logical value (for example None is typecast to False).
In your case, the conditions (namely, comparisons) are done inside your functions (see the return statements), so that the functions already return logical values.
When calling a method, it's executed and return a value, which can be use in different manner : directly or stored in a variable, this code is exactly the same as the following, but just not saving in variables and inline the methods in the condition
disck_check = disk_check_usage("/") # True or False
cpu_check = check_cpu_usage() # True or False
if not disk_check or not cpu_check: # Boolean conbination
print("ERROR!")
else:
print("Everything is OK")
You have to use 'and' operator instead the 'or' used by you.
def disk_check_usage(disk):
du = shutil.disk_usage(disk)
free = du.free/du.total * 100
return free > 20
def check_cpu_usage():
usage = psutil.cpu_percent(1)
return usage < 75
if not disk_check_usage("/") and not check_cpu_usage():
print("ERROR!")
else:
print("Everything is OK")

Why must I cast int to bool before checking "is True"?

In my url.py file in Django, I have the following line:
if bool(settings.DEBUG) is True:
This works well because settings.DEBUG == 1 therefore it equals True after the bool() function. However, I needed to add the bool() to make it work. The original line was not working and the original line was:
if settings.DEBUG is True:
This always evaluates to False, even when settings.DEBUG == 1.
Why is the value 1 evaluating to False when I do not cast it explicitly to bool?
In other words, why doesn't "1 is True" evaluate true?
Note that I am using python 3 in case that matters to the answer.
At first glance, this can be confusing as of course, the value of '1' should equal true right? However what if this int value equals '99' should this be true also?
By comparing an int with a Boolean value within the python compiler it is like comparing apples with oranges which isn't possible therefore defaulting to 'false'.
By wrapping your int value in a bool(function) you are declaring to the python compiler treat this int value as a boolean (0 == False, 1 == True) therefore it works as you intended.
Hope this helps :)

Python's Evaluation of If-Else Conditional Statements

I recently encountered an example of an if-else conditional statement and could not understand the rationale behind its output. The following are the statements:
if 0:
1
else:
2
Output: 2
I tried different integers in 0's place, and received 1 each time. Is this because the zero in the if condition represents False? But then why do integers other than 1 still satisfy the if condition?
Thanks!
Edit: Thank you for all your answers. I now understand that any integer except 0 in the 'if' statement will make the statement True by default, resulting in an output of 1, in this case.
Python will always attempt to determine the "truthiness" of a given value used in a boolean context. In Python any numerical value of 0 (or 0.0) is considered false, and string, dictionary, list, or other iterable (or other class that can report its length) is false if it's empty or has length of 0. Also, None and boolean False are considered false.
Other values are considered true.
More details: https://docs.python.org/2.4/lib/truth.html.
In Python, bool is a subtype of int. False has the value 0, while other non-zero integers have the subtype bool with the value True.
To see this for yourself try this: False == 0
And to see the subtypes of int try this: int.__subclasses__()
1 is considered True while 0 is False,just like in binary.
Any non-zero numeric value is evaluated as True in a conditional statement.
bool  type is just a subtype of int in Python, with 1 == True and 0 == False.

"4 and 5" is 5, while "4 or 5" is 4. Is there any reason? [duplicate]

This question already has answers here:
Does Python support short-circuiting?
(3 answers)
How do "and" and "or" act with non-boolean values?
(8 answers)
Closed 9 years ago.
When I test the difference between and and or, I meet this problem. Could you please help me understand it?
This behavior is a weird quirk that comes out of three different features of python code. Non-zeros values are true, logical operation evaluation, and short-circuiting. I'll explain these features below:
Non-Zero Values are True
The first thing you have to know in evaluating these expressions is that they are logical operators. They are designed to work with true or false values:
true and true = true
true and false = false
false and true = false
false and false = false
true or true = true
true or false = true
false or true = true
false or false = false
However, python (and many languages) allow you to put any value in. So long as they are non-zero they are considered true. So:
4 and 5 = true
4 and 0 = false
This is normal so far. Most languages have this.
Logical Operation Evaluation
Here Python does something a little unique. Instead of returning true or false, it actually returns the value of the last item it checked in the statement. So:
4 and 5 = 5 (which will be evaluated as true)
To fully understand which value will actually get returned you have to also understand:
Short-Circuiting
When evaluating these logical operators, the compiler can often stop early. Take the example:
3 or 4
We know that the statement will return true, but which value will it return? To figure this out you have to understand which value will be the last one looked at. The system will look at 3, and realize that the statement is true. It doesn't matter what the second value is, 3 or anything is true. So the value returned is 3 because it is the last value checked.
However, if we use and:
3 and 4
Once we look at 3, we still need to check that the second value is true. It could makes a difference. So the second value is evaluated. If it is true, it returns the last value looked at, in this case 4.
In Conclusion
You just have to think about which value the interpreter can stop on.
3 or 4 = 3 // because we can stop at 3
3 and 4 = 4 // because we have to check the 4
0 or 3 = 3 // because we needed to check the 3
Yes, the and operator requires all arguments to be true, and returns the last one checked, which is 5. (If any of the arguments were false, it would return the first false value, since that would be the last one checked in order to verify if all arguments were true.)
The or operator requires only one argument to be true, and returns the last one checked, which is 4, because 4 represents the first true value in the conditional. (If all arguments were false, then the return value would be equal to the last false value, since that would be the last value checked in order to verify if any of the arguments were true.)
true1 and true2 >>>true2
true1 or true2 >>>true1
when runinng true1 and true2, python must check the value returned by every expression is true or not, so it will return the last one.
but when running true1 or true2 , as true1 retrun "true" (in your example, 4 is "true") already, so it is unncessary to continue checking the rest.
I think the way to look at it is at a simpler level, which was designed for optimization.
and requires that both sides be "truthy." It checks the left side. If it is "truthy," it returns the second value without checking what it is.
or requires only one side to be "truthy." It checks the first side. If it is "truthy," it returns it. If not, it returns the second side, again without checking it.
For "4 and 5", because 4 is "truthy," it returns 5.
For "4 or 5", it returns 4 without even looking at 5.
Need proof? Make these functions:
def four():
print 'Evaluating four!'
return 4
def five():
print 'Evaluating five!'
return 5
Now see what gets printed:
>>> four() and five()
Evaluating four!
Evaluating five!
5
and evaluated four(), and since it was true it returned five().
>>> left() or right()
Evaluating left side!
4
or evaluated four() and since it was true, returned four() without even calling five()!

Pythonic way of checking for 0 in if statement?

In coding a primality tester, I came across an interesting thought. When you want to do something if the result of an operation turns out to be 0, which is the better ('pythonic') way of tackling it?
# option A - comparison
if a % b == 0:
print('a is divisible by b')
# option B - is operator
if a % b is 0:
print('a is divisible by b')
# option C - boolean not
if not a % b:
print('a is divisible by b')
PEP 8 says that comparisons to singletons like None should be done with the is operator. It also says that checking for empty sequences should use not, and not to compare boolean values with == or is. However, it doesn't mention anything about checking for a 0 as a result.
So which option should I use?
Testing against 0 is (imo) best done by testing against 0. This also indicates that there might be other values than just 0 and 1.
If the called function really only returns 0 on success and 1 on fail to say Yes/No, Success/Failure, True/False, etc., then I think the function is the problem and should (if applicable) be fixed to return True and False instead.
just personal : I prefer the not a % b way because it seems to be highly readable. But now, to lower the confusion level in the code, I will use the == 0, as it express what you expect to test exactly in a more accurate way. It's the "care for debug" approach.
0 isn't guaranteed to be a singleton so don't use is to test against it: currently C Python re-uses small integers so there is probably only one int with the value 0 plus a long if you're still on Python 2.x, and any number of float zeroes not to mention False which all compare equal to 0. Some earlier versions of Python, before it got a separate bool type used a different int zero for the result of comparisons.
Use either == (which would be my preference) or just the not, whichever you prefer.
A and C are both valid and very pythonic.
B is not, because
0 semantically is not a singleton (it is in cPython, but that is an implementation detail).
It will not work with float a or b.
It is actually possible that this will not work in some other implementation of Python.

Categories

Resources