Here is a little example of boolean expressions (you don't have to type it in):
a=6b=7c=42print(1,a==6)print(2,a==7)print(3,a==6andb==7)print(4,a==7andb==7)print(5,nota==7andb==7)print(6,a==7orb==7)print(7,a==7orb==6)print(8,not(a==7andb==6))print(9,nota==7andb==6)
With the output being:
1 True2 False3 True4 False5 True6 True7 False8 True9 False
What is going on? The program consists of a bunch of funny lookingprint statements. Eachprint statement prints a number and an expression. The number is to help keep track of which statement I am dealing with. Notice how each expression ends up being eitherFalse orTrue. In Python false can also be written as 0 and true as 1.
The lines:
print(1,a==6)print(2,a==7)
print out aTrue and aFalse respectively just as expected since the first is true and the second is false. The third print,print(3, a == 6 and b == 7), is a little different. The operatorand means if both the statement before and the statement after are true then the whole expression is true otherwise the whole expression is false. The next line,print(4, a == 7 and b == 7), shows how if part of anand expression is false, the whole thing is false. The behavior ofand can be summarized as follows:
| expression | result |
|---|---|
trueand true | true |
trueand false | false |
falseand true | false |
falseand false | false |
Notice that if the first expression is false Python does not check the second expression since it knows the whole expression is false. Try runningFalse and print("Hi") and compare this to runningTrue and print("Hi") The technical term for this isshort-circuit evaluation
The next line,print(5, not a == 7 and b == 7), uses thenot operator.not just gives the opposite of the expression. (The expression could be rewritten as print(5, a != 7 and b == 7)). Here is the table:
| expression | result |
|---|---|
not true | false |
not false | true |
The two following lines,print(6, a == 7 or b == 7) andprint(7, a == 7 or b == 6), use theor operator. Theor operator returns true if the first expression is true, or if the second expression is true or both are true. If neither are true it returns false. Here's the table:
| expression | result |
|---|---|
trueor true | true |
trueor false | true |
falseor true | true |
falseor false | false |
Notice that if the first expression is true Python doesn't check the second expression since it knows the whole expression is true. This works sinceor is true if at least one half of the expression is true. The first part is true so the second part could be either false or true, but the whole expression is still true.
The next two lines,print(8, not (a == 7 and b == 6)) andprint(9, not a == 7 and b == 6), show that parentheses can be used to group expressions and force one part to be evaluated first. Notice that the parentheses changed the expression from false to true. This occurred since the parentheses forced thenot to apply to the whole expression instead of just thea == 7 portion.
Here is an example of using a boolean expression:
list=["Life","The Universe","Everything","Jack","Jill","Life","Jill"]# make a copy of the list. See the More on Lists chapter to explain what [:] means.copy=list[:]# sort the copycopy.sort()prev=copy[0]delcopy[0]count=0# go through the list searching for a matchwhilecount<len(copy)andcopy[count]!=prev:prev=copy[count]count=count+1# If a match was not found then count can't be < len# since the while loop continues while count is < len# and no match is foundifcount<len(copy):print("First Match:",prev)
And here is the output:
First Match: Jill
This program works by continuing to check for matchwhile count < len(copy) and copy[count] is not equal to prev. When eithercount is greater than the last index ofcopy or a match has been found theand is no longer true so the loop exits. Theif simply checks to make sure that thewhile exited because a match was found.
The other "trick" ofand is used in this example. If you look at the table forand notice that the third entry is "false and false". Ifcount >= len(copy) (in other wordscount < len(copy) is false) thencopy[count] is never looked at. This is because Python knows that if the first is false then they can't both be true. This is known as a short circuit and is useful if the second half of theand will cause an error if something is wrong. I used the first expression (count < len(copy)) to check and see ifcount was a valid index forcopy. (If you don't believe me remove the matches "Jill" and "Life", check that it still works and then reverse the order ofcount < len(copy) and copy[count] != prev tocopy[count] != prev and count < len(copy).)
Boolean expressions can be used when you need to check two or more different things at once.
A common mistake for people new to programming is a misunderstanding of the way that boolean operators works, which stems from the way the python interpreter reads these expressions. For example, after initially learning about "and " and "or" statements, one might assume that the expressionx == ('a' or 'b') would check to see if the variablex was equivalent to one of the strings'a' or'b'. This is not so. To see what I'm talking about, start an interactive session with the interpreter and enter the following expressions:
>>> 'a' == ('a' or 'b')>>> 'b' == ('a' or 'b')>>> 'a' == ('a' and 'b')>>> 'b' == ('a' and 'b')And this will be the unintuitive result:
>>> 'a' == ('a' or 'b')True>>> 'b' == ('a' or 'b')False>>> 'a' == ('a' and 'b')False >>> 'b' == ('a' and 'b')TrueAt this point, theand andor operators seem to be broken. It doesn't make sense that, for the first two expressions,'a' is equivalent to'a' or'b' while'b' is not. Furthermore, it doesn't make any sense that 'b' is equivalent to'a' and'b'. After examining what the interpreter does with boolean operators, these results do in fact exactly what you are asking of them, it's just not the same as what you think you are asking.
When the Python interpreter looks at anor expression, it takes the first statement and checks to see if it is true. If the first statement is true, then Python returns that object's value without checking the second statement. This is because for anor expression, the whole thing is true if one of the values is true; the program does not need to bother with the second statement. On the other hand, if the first value is evaluated as false Python checks the second half and returns that value. That second half determines the truth value of the whole expression since the first half was false. This "laziness" on the part of the interpreter is called "short circuiting" and is a common way of evaluating boolean expressions in many programming languages.
Similarly, for anand expression, Python uses a short circuit technique to speed truth value evaluation. If the first statement is false then the whole thing must be false, so it returns that value. Otherwise if the first value is true it checks the second and returns that value.
One thing to note at this point is that the boolean expression returns a value indicatingTrue orFalse, but that Python considers a number of different things to have a truth value assigned to them. To check the truth value of any given objectx, you can use the fuctionbool(x) to see its truth value. Below is a table with examples of the truth values of various objects:
| True | False |
|---|---|
| True | False |
| 1 | 0 |
| Numbers other than zero | The string 'None' |
| Nonempty strings | Empty strings |
| Nonempty lists | Empty lists |
| Nonempty dictionaries | Empty dictionaries |
Now it is possible to understand the perplexing results we were getting when we tested those boolean expressions before. Let's take a look at what the interpreter "sees" as it goes through that code:
First case:
>>> 'a' == ('a' or 'b') # Look at parentheses first, so evaluate expression "('a' or 'b')" # 'a' is a nonempty string, so the first value is True # Return that first value: 'a'>>> 'a' == 'a' # the string 'a' is equivalent to the string 'a', so expression is TrueTrueSecond case:
>>> 'b' == ('a' or 'b') # Look at parentheses first, so evaluate expression "('a' or 'b')" # 'a' is a nonempty string, so the first value is True # Return that first value: 'a'>>> 'b' == 'a' # the string 'b' is not equivalent to the string 'a', so expression is FalseFalseThird case:
>>> 'a' == ('a' and 'b') # Look at parentheses first, so evaluate expression "('a' and 'b')" # 'a' is a nonempty string, so the first value is True, examine second value # 'b' is a nonempty string, so second value is True # Return that second value as result of whole expression: 'b'>>> 'a' == 'b' # the string 'a' is not equivalent to the string 'b', so expression is FalseFalseFourth case:
>>> 'b' == ('a' and 'b') # Look at parentheses first, so evaluate expression "('a' and 'b')" # 'a' is a nonempty string, so the first value is True, examine second value # 'b' is a nonempty string, so second value is True # Return that second value as result of whole expression: 'b'>>> 'b' == 'b' # the string 'b' is equivalent to the string 'b', so expression is TrueTrueSo Python was really doing its job when it gave those apparently bogus results. As mentioned previously, the important thing is to recognize what value your boolean expression will return when it is evaluated, because it isn't always obvious.
Going back to those initial expressions, this is how you would write them out so they behaved in a way that you want:
>>> 'a' == 'a' or 'a' == 'b'True>>> 'b' == 'a' or 'b' == 'b'True>>> 'a' == 'a' and 'a' == 'b'False>>> 'b' == 'a' and 'b' == 'b'False
When these comparisons are evaluated they return truth values in terms of True or False, not strings, so we get the proper results.
An accelerometer is a device which can be used to detect the acceleration of an object, or the force of gravity. For instance, when you are sitting in a car and the driver steps on the gas pedal, you can feel a force pushing you back into your seat. Or, as you are sitting in your chair right now, you can feel Earth's gravity pulling you down, towards the center of the earth. The Linkbot can also feel these forces acting on it using its accelerometer, and you can get the accelerometer values from the Linkbot.
Lets take a look at the Linkbot'sgetAccelerometerData() function.
importbarobodongle=barobo.Dongle()dongle.connect()myLinkbot=dongle.getLinkbot()accel=myLinkbot.getAccelerometerData()print(accel)
Output
[0.005859375, -0.1005859375, 0.9755859375]
This small program connects to a Linkbot, gets its accelerometer data, and prints it out. The number you get may be different than the ones shown, depending on the orientation and acceleration of your Linkbot when you ran the program.
Notice that the value isn't a single value, but is actually a list of 3 values. Each one of these values is the force (in G's) that the Linkbot is currently experiencing on a particular axis direction, corresponding to the x, y, and z axes of the Linkbot.
We can calculate the total magnitude of force that the Linkbot is experiencing through this equation:
Lets write a Python function that calculates the magnitude of acceleration given a list of three acceleration values:
importmath# for math.sqrt(), the square-root functiondefaccelMag(accel):returnmath.sqrt(accel[0]**2+accel[1]**2+accel[2]**2)importbarobodongle=barobo.Dongle()dongle.connect()myLinkbot=dongle.getLinkbot('ABCD')# Replace 'ABCD' with your Linkbot's Serial IDaccel=myLinkbot.getAccelerometerData()# Get the accel valuesprint(accel)# print accel valuesprint(accelMag(accel))# print total magnitude of accel values
Output:
[0.0078125, -0.1015625, 0.974609375]0.9799180631054775
Note that the units of these numbers is in earth-gravitational units, usually called "G's". When the above output was produced, the code was executed on a Linkbot that was sitting still on a desk. Because the Linkbot is only being pulled on by Earth's gravity, we expect its magnitude to be very close to 1. If the Linkbot were weightless, floating in space, or in freefall, we can expect the magnitude to be close to zero.
Lets try tilting our Linkbot on a diagonal and running it again. We should expect the three numbers in the list to change, but the total magnitude should still be very close to 1.
Output:
[0.72265625, -0.6875, -0.0244140625]0.997739621400201
As we can see, theaccelMag() function that we wrote can display the total magnitude of acceleration acting on the Linkbot regardless of direction. That means we can use it to detect free-fall or high acceleration events, like if the Linkbot is dropped, or bumps into something.
Now, lets try to write a program that makes the Linkbot beep if it detects that it is in freefall.Notice, though, that the values reported by the accelerometer have some "noise". Noise is tiny amounts of error that are picked up at random by robotic sensors. What this means is that the magnitude reported by the Linkbot will almost never exactly zero or exactly 1, even if the Linkbot is in free-fall or sitting still on your desk. What this means is when we write our program, we don't want to check to see if the acceleration magnitude is zero. Instead, we want to check to see if it is below a certain threshold; say, 0.2 G's.
importmath# for math.sqrt()defaccelMag(accel):returnmath.sqrt(accel[0]**2+accel[1]**2+accel[2]**2)importbaroboimporttime# for time.sleep()dongle=barobo.Dongle()dongle.connect()myLinkbot=dongle.getLinkbot('ABCD')# Replace 'ABCD' with your Linkbot's Serial IDprint('Gently toss your Linkbot into the air. Type Ctrl-C to quit the program.')whileTrue:accel=myLinkbot.getAccelerometerData()ifaccelMag(accel)<0.2:# 1myLinkbot.setBuzzerFrequency(440)# 2print('Wheeee!')time.sleep(1)myLinkbot.setBuzzerFrequency(0)
Notice that we've now added an infinite loop to the program. The loop repeatedly checks the accelerometer magnitude at "# 1". If the magnitude is less than 0.2, it beeps the buzzer for 1 second at "# 2".
password1.py
## This program asks a user for a name and a password.# It then checks them to make sure that the user is allowed in.name=input("What is your name? ")password=input("What is the password? ")ifname=="Josh"andpassword=="Friday":print("Welcome Josh")elifname=="Fred"andpassword=="Rock":print("Welcome Fred")else:print("I don't know you.")
Sample runs
What is your name?JoshWhat is the password?FridayWelcome Josh
What is your name?BillWhat is the password?MoneyI don't know you.
Write a program that has a user guess your name, but they only get 3 chances to do so until the program quits.
print("Try to guess my name!")count=1name="guilherme"guess=input("What is my name? ")whilecount<3andguess.lower()!=name:# .lower allows things like Guilherme to still matchprint("You are wrong!")guess=input("What is my name? ")count=count+1ifguess.lower()!=name:print("You are wrong!")# this message isn't printed in the third chance, so we print it nowprint("You ran out of chances.")else:print("Yes! My name is",name+"!")
Write a Linkbot program that beeps if the acceleration exceeds 2 G's. This typically happens if the robot is bumped, or when the robot is shaken vigorously.
importmathdefaccelMag(accel):returnmath.sqrt(accel[0]**2+accel[1]**2+accel[2]**2)importbaroboimporttime# For time.sleep()dongle=barobo.Dongle()dongle.connect()myLinkbot=dongle.getLinkbot('ABCD')# Change 'ABCD' to your Linkbot's Serial IDprint('Shake or bump your Linkbot. Type Ctrl-C to quit the program.')whileTrue:accel=myLinkbot.getAccelerometerData()ifaccelMag(accel)>2:myLinkbot.setBuzzerFrequency(440)print('Ow!')time.sleep(1)myLinkbot.setBuzzerFrequency(0)
| Learning Python 3 with the Linkbot | ||
| ← For Loops | Boolean Expressions | Dictionaries → |