跳轉到內容

使用 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" 語句示例'
華夏公益教科書