跳转到内容

使用 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 语句打印一个数字和一个表达式。这个数字是为了帮助跟踪我正在处理的是哪条语句。注意每个表达式最终都是 FalseTrue。在 Python 中,false 可以写成 0,true 可以写成 1。

代码

print(1, a == 6)
print(2, a == 7)

分别打印出 TrueFalse,正如预期的那样,因为第一个是真,第二个是假。第三个 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

此时,andor 运算符似乎坏了。对于前两个表达式,'a' 等效于 'a''b',而 'b' 不等效,这没有道理。此外,'b' 等效于 'a''b' 也没有道理。在检查了解释器如何处理布尔运算符之后,这些结果实际上确实执行了您所要求的操作,只是与您认为您所要求的操作不同。

当 Python 解释器遇到 or 表达式时,它会获取第一个语句并检查它是否为真。如果第一个语句为真,则 Python 返回该对象的 value,而无需检查第二个语句。这是因为对于 or 表达式,如果其中一个 value 为真,则整个表达式为真;程序无需理会第二个语句。另一方面,如果第一个 value 被评估为假,Python 会检查第二部分并返回该 value。该第二部分决定了整个表达式的真值,因为第一部分为假。解释器这种“懒惰”被称为“短路”,是许多编程语言中评估布尔表达式的常用方法。

类似地,对于 and 表达式,Python 使用短路技术来加快真值评估。如果第一个语句为假,则整个表达式必须为假,因此它返回该 value。否则,如果第一个 value 为真,它会检查第二个并返回该 value。

需要注意的一点是,布尔表达式返回一个表示 TrueFalse 的 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 获取加速度计值。

让我们看看 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)


使用 Linkbot 学习 Python 3
 ← For 循环 布尔表达式 字典 → 
华夏公益教科书