使用 Linkbot 学习 Python 3/布尔表达式
这里有一个布尔表达式的小例子(你不需要输入它)
a = 6
b = 7
c = 42
print(1, a == 6)
print(2, a == 7)
print(3, a == 6 and b == 7)
print(4, a == 7 and b == 7)
print(5, not a == 7 and b == 7)
print(6, a == 7 or b == 7)
print(7, a == 7 or b == 6)
print(8, not (a == 7 and b == 6))
print(9, not a == 7 and b == 6)
输出为
1 True 2 False 3 True 4 False 5 True 6 True 7 False 8 True 9 False
发生了什么?程序由一组看起来很奇怪的 print
语句组成。每个 print
语句打印一个数字和一个表达式。这个数字是为了帮助跟踪我正在处理的是哪条语句。注意每个表达式最终都是 False
或 True
。在 Python 中,false 可以写成 0,true 可以写成 1。
代码
print(1, a == 6)
print(2, a == 7)
分别打印出 True
和 False
,正如预期的那样,因为第一个是真,第二个是假。第三个 print 语句 print(3, a == 6 and b == 7)
有点不同。运算符 and
的意思是,如果前后的语句都为真,则整个表达式为真,否则整个表达式为假。下一行 print(4, a == 7 and b == 7)
显示了如果 and
表达式的一部分为假,则整个表达式都为假。and
的行为可以总结如下
表达式 | 结果 |
---|---|
true and true |
true |
true and false |
false |
false and true |
false |
false and false |
false |
请注意,如果第一个表达式为假,Python 不会检查第二个表达式,因为它知道整个表达式为假。尝试运行 False and print("Hi")
并将其与运行 True and print("Hi")
进行比较。这个技术的专业术语是 短路求值
下一行 print(5, not a == 7 and b == 7)
使用了 not
运算符。not
只给出表达式的反义。(这个表达式可以改写为 print(5, a != 7 and b == 7)
)。这里有表格
表达式 | 结果 |
---|---|
not true |
false |
not false |
true |
接下来的两行 print(6, a == 7 or b == 7)
和 print(7, a == 7 or b == 6)
使用了 or
运算符。or
运算符如果第一个表达式为真,或者第二个表达式为真,或者两者都为真,则返回真。如果两者都不为真,则返回假。以下是表格
表达式 | 结果 |
---|---|
true or true |
true |
true or false |
true |
false or true |
true |
false or false |
false |
请注意,如果第一个表达式为真,Python 不会检查第二个表达式,因为它知道整个表达式为真。这是因为 or
在至少有一半表达式为真的情况下为真。第一部分为真,所以第二部分可以是假或真,但整个表达式仍然为真。
接下来的两行 print(8, not (a == 7 and b == 6))
和 print(9, not a == 7 and b == 6)
显示了可以使用括号将表达式分组并强制先计算其中一部分。请注意,括号将表达式从假改为真。这是因为括号强制 not
应用于整个表达式,而不是只应用于 a == 7
部分。
这是一个使用布尔表达式的示例
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 copy
copy.sort()
prev = copy[0]
del copy[0]
count = 0
# go through the list searching for a match
while count < len(copy) and copy[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 found
if count < len(copy):
print("First Match:", prev)
以下是输出
First Match: Jill
这个程序通过不断地检查匹配 while count < len(copy) and copy[count] is not equal to prev
来工作。当 count
大于 copy
的最后一个索引或找到匹配项时,and
不再为真,所以循环退出。if
只检查确保 while
退出是因为找到了匹配项。
本例中还使用了 and
的另一个“技巧”。如果你查看 and
的表格,你会发现第三个条目是“false and false”。如果 count >= len(copy)
(换句话说,count < len(copy)
为假),那么 copy[count]
永远不会被查看。这是因为 Python 知道如果第一个为假,那么它们不可能都为真。这被称为短路,如果 and
的后半部分在出现错误时会导致错误,它将非常有用。我使用第一个表达式 (count < len(copy)
) 来检查 count
是否是 copy
的有效索引。(如果你不相信我,请删除匹配的“Jill”和“Life”,检查它是否仍然有效,然后反转 count < len(copy) and copy[count] != prev
的顺序为 copy[count] != prev and count < len(copy)
。)
当您需要同时检查两个或更多不同事项时,可以使用布尔表达式。
编程新手常犯的一个错误是误解布尔运算符的工作方式,这源于 Python 解释器读取这些表达式的方式。例如,在最初学习了“and”和“or”语句之后,人们可能会认为表达式 x == ('a' or 'b')
会检查变量 x
是否等效于字符串 'a'
或 'b'
之一。事实并非如此。要了解我的意思,请使用解释器启动交互式会话并输入以下表达式
>>> 'a' == ('a' or 'b') >>> 'b' == ('a' or 'b') >>> 'a' == ('a' and 'b') >>> 'b' == ('a' and 'b')
这将是违反直觉的结果
>>> 'a' == ('a' or 'b') True >>> 'b' == ('a' or 'b') False >>> 'a' == ('a' and 'b') False >>> 'b' == ('a' and 'b') True
此时,and
和 or
运算符似乎坏了。对于前两个表达式,'a'
等效于 'a'
或 'b'
,而 'b'
不等效,这没有道理。此外,'b'
等效于 'a'
和 'b'
也没有道理。在检查了解释器如何处理布尔运算符之后,这些结果实际上确实执行了您所要求的操作,只是与您认为您所要求的操作不同。
当 Python 解释器遇到 or
表达式时,它会获取第一个语句并检查它是否为真。如果第一个语句为真,则 Python 返回该对象的 value,而无需检查第二个语句。这是因为对于 or
表达式,如果其中一个 value 为真,则整个表达式为真;程序无需理会第二个语句。另一方面,如果第一个 value 被评估为假,Python 会检查第二部分并返回该 value。该第二部分决定了整个表达式的真值,因为第一部分为假。解释器这种“懒惰”被称为“短路”,是许多编程语言中评估布尔表达式的常用方法。
类似地,对于 and
表达式,Python 使用短路技术来加快真值评估。如果第一个语句为假,则整个表达式必须为假,因此它返回该 value。否则,如果第一个 value 为真,它会检查第二个并返回该 value。
需要注意的一点是,布尔表达式返回一个表示 True
或 False
的 value,但 Python 认为许多不同的事物都有分配给它们的真值。要检查任何给定对象 x
的真值,可以使用函数 bool(x)
来查看其真值。以下是各种对象的真值示例表格
True | False |
---|---|
True | False |
1 | 0 |
非零数字 | 字符串 'None' |
非空字符串 | 空字符串 |
非空列表 | 空列表 |
非空字典 | 空字典 |
现在,我们可以理解之前测试那些布尔表达式时出现的令人困惑的结果。让我们来看看解释器在执行该代码时的“视角”
第一种情况
>>> '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 True True
第二种情况
>>> '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 False False
第三种情况
>>> '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 False False
第四种情况
>>> '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 True True
因此,Python 在给出那些看似错误的结果时,实际上是在正常工作。如前所述,重要的是要认识到您的布尔表达式在评估时会返回什么 value,因为它并不总是显而易见的。
回到那些初始表达式,以下是如何编写它们以使其按您想要的方式执行
>>> 'a' == 'a' or 'a' == 'b' True >>> 'b' == 'a' or 'b' == 'b' True >>> 'a' == 'a' and 'a' == 'b' False >>> 'b' == 'a' and 'b' == 'b' False
当这些比较被评估时,它们会以 True 或 False 的形式返回真值,而不是字符串,所以我们得到了正确的结果。
加速度计是一种可以用来检测物体加速度或重力作用的装置。例如,当您坐在汽车里而司机踩油门时,您会感觉到一股力量将您向后推回座椅。或者,当您现在坐在椅子上时,您会感觉到地球的重力将您拉向地球中心。Linkbot 也可以使用其加速度计来感受作用于它的这些力,您可以从 Linkbot 获取加速度计值。
让我们看看 Linkbot 的 getAccelerometerData()
函数。
import barobo
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot()
accel = myLinkbot.getAccelerometerData()
print(accel)
输出
[0.005859375, -0.1005859375, 0.9755859375]
这个小程序连接到 Linkbot,获取其加速度计数据并打印出来。你得到的数字可能与显示的数字不同,具体取决于你运行程序时 Linkbot 的方向和加速度。
请注意,该值不是一个单一的值,而是一个包含 3 个值的列表。每个值表示 Linkbot 当前在特定轴方向上所受到的力(以 G 为单位),分别对应于 Linkbot 的 x 轴、y 轴和 z 轴。
我们可以通过以下公式计算 Linkbot 所受到的力的总量:
让我们编写一个 Python 函数,根据三个加速度值列表计算加速度的大小。
import math # for math.sqrt(), the square-root function
def accelMag(accel):
return math.sqrt( accel[0]**2 + accel[1]**2 + accel[2]**2 )
import barobo
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Replace 'ABCD' with your Linkbot's Serial ID
accel = myLinkbot.getAccelerometerData() # Get the accel values
print(accel) # print accel values
print(accelMag(accel)) # print total magnitude of accel values
输出
[0.0078125, -0.1015625, 0.974609375] 0.9799180631054775
请注意,这些数字的单位是地球重力单位,通常称为“G”。当生成以上输出时,代码是在静置在桌上的 Linkbot 上执行的。由于 Linkbot 只受到地球重力的作用,我们预计其大小非常接近 1。如果 Linkbot 失重,漂浮在太空中或处于自由落体状态,我们预计其大小会接近零。
让我们尝试以对角线方式倾斜 Linkbot 并再次运行它。我们应该预期列表中的三个数字会发生变化,但总量级仍然应该非常接近 1。
输出
[0.72265625, -0.6875, -0.0244140625] 0.997739621400201
正如我们所看到的,我们编写的 accelMag()
函数可以显示作用在 Linkbot 上的加速度总量,无论其方向如何。这意味着我们可以用它来检测自由落体或高加速度事件,例如 Linkbot 被掉落或撞到东西时。
现在,让我们尝试编写一个程序,如果 Linkbot 检测到它处于自由落体状态,就让它发出蜂鸣声。请注意,加速度计报告的值存在一些“噪声”。噪声是指机器人传感器随机拾取的微量误差。这意味着 Linkbot 报告的大小几乎永远不会完全为零或完全为 1,即使 Linkbot 处于自由落体状态或静置在桌面上。这意味着当我们编写程序时,我们不想检查加速度大小是否为零。相反,我们想检查它是否低于某个阈值;例如,0.2 G。
import math # for math.sqrt()
def accelMag(accel):
return math.sqrt( accel[0]**2 + accel[1]**2 + accel[2]**2 )
import barobo
import time # for time.sleep()
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Replace 'ABCD' with your Linkbot's Serial ID
print('Gently toss your Linkbot into the air. Type Ctrl-C to quit the program.')
while True:
accel = myLinkbot.getAccelerometerData()
if accelMag(accel) < 0.2: # 1
myLinkbot.setBuzzerFrequency(440) # 2
print('Wheeee!')
time.sleep(1)
myLinkbot.setBuzzerFrequency(0)
请注意,我们现在在程序中添加了一个无限循环。循环在“# 1”处重复检查加速度计的大小。如果大小小于 0.2,它会在“# 2”处使蜂鸣器响 1 秒钟。
示例
[edit | edit source]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? ")
if name == "Josh" and password == "Friday":
print("Welcome Josh")
elif name == "Fred" and password == "Rock":
print("Welcome Fred")
else:
print("I don't know you.")
示例运行
What is your name? Josh What is the password? Friday Welcome Josh
What is your name? Bill What is the password? Money I don't know you.
练习
[edit | edit source]编写一个程序,让用户猜你的名字,但他们只有 3 次机会才能猜到,否则程序会退出。
print("Try to guess my name!")
count = 1
name = "guilherme"
guess = input("What is my name? ")
while count < 3 and guess.lower() != name: # .lower allows things like Guilherme to still match
print("You are wrong!")
guess = input("What is my name? ")
count = count + 1
if guess.lower() != name:
print("You are wrong!") # this message isn't printed in the third chance, so we print it now
print("You ran out of chances.")
else:
print("Yes! My name is", name + "!")
编写一个 Linkbot 程序,如果加速度超过 2 G,则发出蜂鸣声。这通常发生在机器人被撞击或剧烈摇晃时。
import math
def accelMag(accel):
return math.sqrt( accel[0]**2 + accel[1]**2 + accel[2]**2 )
import barobo
import time # For time.sleep()
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Change 'ABCD' to your Linkbot's Serial ID
print('Shake or bump your Linkbot. Type Ctrl-C to quit the program.')
while True:
accel = myLinkbot.getAccelerometerData()
if accelMag(accel) > 2:
myLinkbot.setBuzzerFrequency(440)
print('Ow!')
time.sleep(1)
myLinkbot.setBuzzerFrequency(0)