跳转到内容

使用 Linkbot 学习 Python 3/处理不完美

来自维基教科书,开放世界开放书籍

… 或者如何处理错误

[编辑 | 编辑源代码]

使用 with 关闭文件

[编辑 | 编辑源代码]

我们使用“with”语句来打开和关闭文件。[1][2]

with open("in_test.txt", "rt") as in_file:
    with open("out_test.txt", "wt") as out_file:
        text = in_file.read()
        data = parse(text)
        results = encode(data)
        out_file.write(results)
    print( "All done." )

如果在这段代码中的任何地方发生了某种错误(其中一个文件无法访问,parse()函数因损坏的数据而阻塞等等),“with”语句保证所有文件最终都会被正确关闭。关闭文件只是意味着文件被我们的程序“清理”和“释放”,以便它可以在另一个程序中使用。


Clipboard

待办事项
“使用 with 关闭文件”这一部分对于非程序员教程来说是否过于详细?如果是,将其移至其他 Python 维基教科书(主题:Python 编程语言


使用 try 捕获错误

[编辑 | 编辑源代码]

因此,您现在有了完美的程序,它运行得非常完美,除了一个细节,它会在无效的用户输入时崩溃。不要害怕,因为 Python 为您提供了一个特殊的控制结构。它叫做 try,它会尝试做某事。这是一个有问题的程序的示例

print("Type Control C or -1 to exit")
number = 1
while number != -1:
   number = int(input("Enter a number: "))
   print("You entered:", number)

注意,当您输入 @#& 时,它会输出类似以下的内容

Traceback (most recent call last):
 File "try_less.py", line 4, in <module>
   number = int(input("Enter a number: "))
ValueError: invalid literal for int() with base 10: '\\@#&'

如您所见,int() 函数对数字 @#& 不满意(理所当然)。最后一行显示了问题所在;Python 发现了一个 ValueError。我们的程序如何处理这种情况?我们要做的第一件事是:将可能发生错误的地方放在 try 块中,第二件事是:告诉 Python 我们希望如何处理 ValueError。以下程序就是这样做的

print("Type Control C or -1 to exit")
number = 1
while number != -1:
    try:
        number = int(input("Enter a number: "))
        print("You entered:", number)
    except ValueError:
        print("That was not a number.")

现在,当我们运行新程序并提供 @#& 时,它会告诉我们“这不是一个数字”,并继续之前正在做的事情。

当您的程序不断出现您知道如何处理的错误时,请将代码放在 try 块中,并将处理错误的方式放在 except 块中。

生成错误:控制 Linkbot 的速度

[编辑 | 编辑源代码]

我们在前面的示例中已经看到,我们可以编写一个函数,使轮式机器人行驶一定距离。我们还可以使用 setJointSpeed() 函数控制电机转速。setJointSpeed() 函数期望以度/秒为单位的转速,但如果我们可以使用英寸/秒为单位来设置机器人速度,那就更好了。将 英寸/秒转换为 度/秒的数学公式为

其中 是轮子半径。让我们扩展来自 使用 Linkbot 学习 Python 3/定义函数 部分的示例

import barobo
import math # So that we can use math.pi
dongle = barobo.Dongle()
dongle.connect() 
myLinkbot = dongle.getLinkbot('abcd') # Change abcd to your Linkbot's serial ID

def driveDistance(linkbot, distance):
    r = 3.5 / 2 # If you have a wheel that's not 3.5 inches in diameter, change "3.5" to the diameter of your wheel
    degrees = (360) / (2 * math.pi * r) * distance
    linkbot.move(degrees, 0, -degrees)

def setSpeed(linkbot, speed):
    r = 3.5 / 2
    omega = (speed/r) * (180/math.pi)
    linkbot.setJointSpeed(1, omega)
    linkbot.setJointSpeed(3, omega)

setSpeed(myLinkbot, 2.5)     # Sets the speed to 2.5 inches/sec
driveDistance(myLinkbot, 10) # Drives the Linkbot 10 inches forward
driveDistance(myLinkbot, -5) # Drives the Linkbot 5 inches backward

这个示例很好。我们定义了一个名为 setSpeed() 的新函数,它设置 Linkbot 轮式车辆的速度,我们使用它将速度设置为 2.5 英寸/秒。

如果程序员尝试将速度设置为 1000 英寸/秒?或者 1000000 英寸/秒?尽管看到 Linkbot 与一级方程式赛车竞争会很酷,但 Linkbot 的电机在物理上无法超过 200 度/秒。如果速度过高,我们应该设置一个用户可以看到并可能处理的错误。这叫做“引发异常”。引发异常的代码如下所示

def setSpeed(linkbot, speed):
    r = 3.5 / 2
    omega = (speed/r) * (180/math.pi)
    if omega > 200:
        raise Exception('The speed is too high!')
    linkbot.setJointSpeed(1, omega)
    linkbot.setJointSpeed(3, omega)

当引发异常时,函数立即返回异常。这些引发的异常可以被 try/except 块捕获。如果异常发生在 try/except 块之外,整个程序将退出并显示异常的错误消息。在 setSpeed() 函数中,这意味着如果执行了 raise,则两个 setJointSpeed() 语句将被跳过。

当我运行新程序并尝试将速度设置为 1000 英寸/秒时,我得到以下输出

Traceback (most recent call last):
  File "./linkbot_speed.py", line 20, in <module>
    setSpeed(myLinkbot, 1000)     # Sets the speed to 1000 inches/sec
  File "./linkbot_speed.py", line 16, in setSpeed
    raise Exception('The speed is too high!')
Exception: The speed is too high!

现在您可以使用 try/catch 块来处理可能的错误。让我们尝试编写一个程序,它尝试再次将速度设置为 10 英寸/秒,但每次遇到异常时,它都会将请求的速度降低 1 英寸/秒,并再次尝试。

import barobo                                                                    
import math # So that we can use math.pi                                         
dongle = barobo.Dongle()                                                         
dongle.connect()                                                                 
myLinkbot = dongle.getLinkbot('ABCD') # Change ABCD to your Linkbot's serial ID        
                                                                                 
def driveDistance(linkbot, distance):                                            
    r = 3.5 / 2 # If you have a wheel that's not 3.5 inches in diameter, change "3.5" to the diameter of your wheel
    degrees = (360) / (2 * math.pi * r) * distance                               
    linkbot.move(degrees, 0, -degrees)                                           
                                                                                 
def setSpeed(linkbot, speed):                                                    
    r = 3.5 / 2                                                                  
    omega = (speed/r) * (180/math.pi)                                            
    if omega > 200:                                                              
        raise Exception('The speed is too high!')                                
    linkbot.setJointSpeed(1, omega)                                              
    linkbot.setJointSpeed(3, omega)                                              
                                                                                 
requestedSpeed = 10  # 1                                                             
while True:  # 2                                                                     
    try:                                                                         
        print('Trying to set speed to: ' + str(requestedSpeed) + 'inches/sec')
        setSpeed(myLinkbot, requestedSpeed)  # 3 
        print('Success!') 
        break  # 4
    except:                                                                      
        print('Failed.')                                                       
        requestedSpeed -= 1  # 5                                                   

# 6                                                                                 
driveDistance(myLinkbot, 10) # Drives the Linkbot 10 inches forward              
driveDistance(myLinkbot, -5) # Drives the Linkbot 5 inches backward

输出为

Trying to set speed to: 10inches/sec
Failed.
Trying to set speed to: 9inches/sec
Failed.
Trying to set speed to: 8inches/sec
Failed.
Trying to set speed to: 7inches/sec
Failed.
Trying to set speed to: 6inches/sec
Success!

让我们一起逐步执行这个程序,确保我们完全理解正在发生的事情。

  • # 1 : 当我们第一次到达这一行时,我们创建一个名为 requestedSpeed 的新变量,并将其值设置为“10”。
  • # 2 : 进入无限循环
  • # 3 : 尝试设置速度。requestedSpeed 当前为 10,过高。setSpeed() 函数抛出异常。由于我们在 try/except 块中,因此在抛出异常后立即转到 except 块。继续执行 # 5
  • # 5 : 将 requestedSpeed 减少 1。requestedSpeed 现在为 9。这是我们 while 循环的结束,这意味着 Python 将返回到循环的开头。
  • # 3 : 我们再次回到 # 3,除了 requestedSpeed 现在为 9。仍然过高,抛出异常。
  • # 5 : 我们再次将 requestedSpeed 减至 8。
  • # 3 : 仍然过高...
  • # 5 : 减少至 7...
  • # 3 : 仍然过高...
  • # 5 : 减少至 6。
  • # 3 : 现在成功了。由于它成功了,所以没有抛出异常。继续执行 # 4
  • # 4 : 此 break 语句将我们从循环中弹出。继续执行 # 6 和程序的其余部分。

至少更新电话号码程序(在部分 字典 中),以便在用户在菜单中未输入任何数据时不会崩溃。

使用 Linkbot 学习 Python 3
 ← 文件 I/O 处理不完美 递归 → 
  1. "'with' 语句"
  2. 'Python "with" 语句示例'
华夏公益教科书