Python 函式是組織程式碼的基礎單元,有效運用函式能提升程式碼的可讀性和重用性。定義函式使用 def 關鍵字,函式呼叫後會傳回特定值,若無明確傳回則預設為 None。函式作用域的變數在函式傳回後會被銷毀。全域變數可透過 global 關鍵字在函式內部修改。錯誤處理方面,try-except 區塊能捕捉特定錯誤,避免程式當機,而 raise 陳述式則允許自訂錯誤訊息。日誌記錄和除錯工具在開發過程中至關重要,logging 模組能記錄不同層級的訊息,而 debugger 則能逐步執行程式碼,協助找出錯誤。理解這些技巧能有效提升程式碼的健壯性和除錯效率,是 Python 開發的必備技能。
練習題解答
-
函式之所以有優勢,是因為它們可以將程式碼組織成邏輯群組,從而提高程式的可讀性和可維護性。另外,函式還可以避免程式碼冗餘,提高程式碼的重用性。
-
函式的程式碼是在函式被呼叫時執行,而不是在函式被定義時。
-
def陳述式用於建立一個函式。 -
函式是一段可以多次呼叫的程式碼塊,而函式呼叫則是指在程式中某個位置執行這個函式的動作。
-
在Python程式中,只有一個全域範圍,而區域性範圍的數量取決於定義的函式數量。每個函式都有一個自己的區域性範圍。
6. 當函式呼叫傳回時,區域性範圍的變數會發生什麼?
當函式呼叫傳回時,區域性範圍的變數會被銷毀,不再存在。
7. 什麼是傳回值?傳回值可以是表示式的一部分嗎?
傳回值是函式執行後傳回的結果,可以是任何型別的值,包括整數、字串等。傳回值可以用於表示式中,例如 x = foo() + 1,其中 foo() 是一個傳回整數的函式。
8. 如果函式沒有傳回陳述式,則呼叫該函式的傳回值是什麼?
如果函式沒有傳回陳述式,則呼叫該函式的傳回值為 None。
9. 如何強制函式中的變數參考全域變數?
使用 global 關鍵字可以強制函式中的變數參考全域變數,例如 global x。
10. None 的資料型別是什麼?
None 的資料型別是 NoneType。
11. import areallyourpetsnamederic 陳述式做了什麼?
這個陳述式並不合法,可能是打字錯誤或無效的模組名稱。
12. 如果你有一個名為 bacon() 的函式在一個名為 spam 的模組中,如何在匯入 spam 後呼叫它?
可以使用 spam.bacon() 呼叫函式。
13. 如何防止程式在遇到錯誤時當機?
可以使用 try-except 區塊來捕捉錯誤,並在 except 區塊中處理錯誤。
14. try 區塊中放什麼?except 區塊中放什麼?
try 區塊中放可能發生錯誤的程式碼,except 區塊中放錯誤處理程式碼。
15. 撰寫一個名為 notrandomdice.py 的程式,並執行它。為什麼每次函式呼叫都傳回相同的數字?
import random
random_number = random.randint(1, 6)
def get_random_dice_roll():
return random_number
print(get_random_dice_roll())
print(get_random_dice_roll())
print(get_random_dice_roll())
print(get_random_dice_roll())
每次函式呼叫都傳回相同的數字,因為 random_number 只在程式啟動時生成一次,並不在每次函式呼叫時重新生成。
練習程式
Collatz 序列
def collatz(number):
if number % 2 == 0:
print(number // 2, end=' ')
return number // 2
else:
print(3 * number + 1, end=' ')
return 3 * number + 1
number = int(input("Enter number: "))
while number!= 1:
number = collatz(number)
這個程式會不斷呼叫 collatz() 函式,直到傳回值為 1。
使用 try-except 處理非整數輸入
在處理使用者輸入時,經常會遇到非整數輸入的情況。為了處理這種情況,我們可以使用 try-except 陳述式來捕捉 ValueError 錯誤。
範例程式碼
def get_integer_input():
while True:
try:
user_input = int(input("請輸入一個整數:"))
return user_input
except ValueError:
print("您輸入的不是整數,請再試一次。")
# 測試程式碼
user_input = get_integer_input()
print("您輸入的整數是:", user_input)
在這個範例中,我們定義了一個 get_integer_input 函式,該函式使用 while 迴圈不斷要求使用者輸入整數,直到使用者輸入有效的整數。當使用者輸入非整數時,程式碼會捕捉 ValueError 錯誤,並印出錯誤訊息,然後再次要求使用者輸入。
使用 raise 陳述式自訂錯誤訊息
在某些情況下,我們可能需要自訂錯誤訊息,以便更好地瞭解程式執行中的錯誤。為此,我們可以使用 raise 陳述式來引發自訂的錯誤。
範例程式碼
def divide(a, b):
if b == 0:
raise ValueError("除數不能為零")
return a / b
# 測試程式碼
try:
result = divide(10, 0)
print("結果:", result)
except ValueError as e:
print("錯誤:", e)
在這個範例中,我們定義了一個 divide 函式,該函式在除數為零時引發 ValueError 錯誤,並自訂錯誤訊息。當我們呼叫 divide 函式時,如果除數為零,程式碼會捕捉 ValueError 錯誤,並印出自訂的錯誤訊息。
除錯工具和技術
除錯是程式設計中非常重要的一部分。透過使用適當的工具和技術,我們可以更快速、更有效地找到並修復程式中的錯誤。
使用 debugger
Debugger 是一個強大的工具,允許我們一步一步地執行程式,並在執行過程中檢查變數的值。這樣,我們就可以更好地瞭解程式的執行流程和錯誤的原因。
範例程式碼
def add(a, b):
result = a + b
return result
# 測試程式碼
result = add(2, 3)
print("結果:", result)
在這個範例中,我們可以使用 debugger 步步執行 add 函式,並檢查變數 result 的值,以便更好地瞭解程式的執行流程。
logging 和 assertions
Logging 和 assertions 是兩種有用的工具,可以幫助我們偵測和修復程式中的錯誤。
範例程式碼
import logging
def divide(a, b):
if b == 0:
logging.error("除數不能為零")
return None
return a / b
# 測試程式碼
result = divide(10, 0)
if result is None:
print("錯誤:除數不能為零")
在這個範例中,我們使用 logging 來記錄錯誤訊息,並使用 assertions 來檢查變數的值。如果除數為零,程式碼會記錄錯誤訊息並傳回 None。
自定義盒子印刷函式
def 自定義盒子印刷(符號, 寬度, 高度):
"""
這個函式用於印刷一個自定義的盒子。
引數:
符號 (str): 盒子的邊框符號,必須是一個單一字元。
寬度 (int): 盒子的寬度,必須大於 2。
高度 (int): 盒子的高度,必須大於 2。
Raises:
Exception: 如果符號不是一個單一字元,或者寬度或高度不大於 2。
"""
# 檢查符號是否是一個單一字元
if len(符號)!= 1:
raise Exception('符號必須是一個單一字元')
# 檢查寬度是否大於 2
if 寬度 <= 2:
raise Exception('寬度必須大於 2')
# 檢查高度是否大於 2
if 高度 <= 2:
raise Exception('高度必須大於 2')
# 印刷盒子的上邊框
print(符號 * 寬度)
# 印刷盒子的內容
for i in range(高度 - 2):
print(符號 + (' ' * (寬度 - 2)) + 符號)
# 印刷盒子的下邊框
print(符號 * 寬度)
try:
自定義盒子印刷('*', 4, 4)
自定義盒子印刷('O', 20, 5)
自定義盒子印刷('x', 1, 3)
except Exception as 錯誤:
print(f"發生錯誤:{錯誤}")
內容解密:
這個程式碼定義了一個名為 自定義盒子印刷 的函式,該函式用於印刷一個自定義的盒子。函式接受三個引數:符號、寬度 和 高度。函式首先檢查 符號 是否是一個單一字元,如果不是,則引發一個 Exception。然後,函式檢查 寬度 和 高度 是否大於 2,如果不是,則引發一個 Exception。如果所有檢查都透過,函式就會印刷出一個盒子,盒子的上邊框和下邊框由 符號 組成,內容由空格組成。
圖表翻譯:
@startuml
:開始; --> :檢查符號;
:B; --> :檢查寬度;
:C; --> :檢查高度;
:D; --> :印刷盒子;
:E; --> :結束;
@enduml
這個流程圖展示了 自定義盒子印刷 函式的執行流程。首先,函式檢查 符號 是否是一個單一字元,如果不是,則引發一個 Exception。然後,函式檢查 寬度 和 高度 是否大於 2,如果不是,則引發一個 Exception。如果所有檢查都透過,函式就會印刷出一個盒子。
錯誤處理和斷言
在程式設計中,錯誤處理和斷言是兩個非常重要的概念。錯誤處理允許我們在程式執行時發生錯誤時進行捕捉和處理,而斷言則是我們用來檢查程式是否正確執行的工具。
錯誤處理
Python 中的 try 和 except 陳述式可以用來處理錯誤。當我們預期某段程式碼可能會發生錯誤時,我們可以將其放在 try 區塊中,並使用 except 區塊來捕捉和處理這些錯誤。
try:
box_print('ZZ', 3, 3)
except Exception as err:
print('An exception happened: ' + str(err))
在這個例子中,如果 box_print 函式發生任何錯誤,錯誤訊息將被捕捉並印出。
斷言
斷言是用來檢查程式是否正確執行的工具。它們可以用來檢查某些條件是否為真,如果條件不為真,則會引發 AssertionError 例外。
assert condition, "Assertion failed"
斷言通常用於檢查函式的輸入引數、變數的值等是否正確。例如:
def add(a, b):
assert isinstance(a, (int, float)), "a must be a number"
assert isinstance(b, (int, float)), "b must be a number"
return a + b
在這個例子中,如果 a 或 b 不是數字,則會引發 AssertionError 例外。
錯誤處理和日誌記錄
在 Python 中,錯誤處理和日誌記錄是兩個非常重要的概念。錯誤處理可以幫助我們捕捉和處理程式執行中的錯誤,而日誌記錄可以幫助我們追蹤程式的執行過程和錯誤資訊。
錯誤處理
Python 中的 assert 陳述式可以用來檢查程式中的條件是否正確,如果條件不正確,則會引發一個 AssertionError。例如:
ages = [26, 57, 92, 54, 22, 15, 17, 80, 47, 73]
ages.sort()
assert ages[0] <= ages[-1]
如果 ages 列表中的第一個元素大於最後一個元素,則會引發一個 AssertionError。
日誌記錄
Python 中的 logging 模組可以用來記錄程式的執行過程和錯誤資訊。例如:
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
logging.debug('Start of program')
這會設定日誌記錄的級別為 DEBUG,並指定日誌記錄的格式。然後,我們可以使用 logging.debug() 函式來記錄程式的執行過程。
範例
下面是一個範例程式,示範如何使用 assert 和 logging:
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
def factorial(n):
logging.debug('Start of factorial(' + str(n) + ')')
if n < 0:
logging.error('n must be a non-negative integer')
return None
elif n == 0:
logging.debug('Base case: n = 0')
return 1
else:
logging.debug('Recursive case: n = ' + str(n))
return n * factorial(n-1)
logging.debug('Start of program')
result = factorial(5)
logging.debug('Result: ' + str(result))
這個程式會計算 5 的階乘,並記錄程式的執行過程和錯誤資訊。
內容解密:
assert陳述式可以用來檢查程式中的條件是否正確。logging模組可以用來記錄程式的執行過程和錯誤資訊。- 日誌記錄的級別可以設定為
DEBUG、INFO、WARNING、ERROR或CRITICAL。 - 日誌記錄的格式可以指定為
%(asctime)s - %(levelname)s - %(message)s。
圖表翻譯:
@startuml
:Start of program; --> :Calculate factorial;
:B; --> :Check if n is non-negative;
:C; --> :Base case: n = 0;
:C; --> :E;
:E; --> :Calculate factorial;
:F; --> :Return result;
:G; --> :End of program;
@enduml
這個圖表示範了程式的執行過程和控制流程。
使用logging模組進行除錯
在Python中,logging模組是一種強大的工具,能夠幫助我們進行除錯和記錄程式的執行過程。下面是一個簡單的範例,展示如何使用logging模組:
import logging
# 設定logging模組的基本組態
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
# 定義一個函式,計算階乘
def factorial(n):
total = 1
for i in range(1, n + 1):
total *= i
logging.debug('i is %d, total is %d', i, total)
logging.debug('End of factorial(%d)', n)
return total
# 測試函式
print(factorial(5))
# 記錄程式的結束
logging.debug('End of program')
這個範例中,我們使用logging模組來記錄函式factorial的執行過程。logging模組會自動記錄每個log訊息的時間、等級和內容。
將log訊息寫入檔案
如果你想將log訊息寫入檔案,而不是顯示在螢幕上,你可以使用filename引數來指設定檔名:
logging.basicConfig(filename='myProgramLog.txt',
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
這樣,log訊息就會被寫入myProgramLog.txt檔案中。
使用logging模組的優點
使用logging模組有以下優點:
- 可以輕鬆地在程式中新增log訊息
- 可以根據需要顯示或隱藏log訊息
- 可以將log訊息寫入檔案中
常見的錯誤
有一些常見的錯誤,可能會導致程式出現問題。例如,以下程式碼中,for迴圈的初始值設定為0,而不是1:
for i in range(n + 1):
total *= i
這會導致函式factorial傳回錯誤的結果。正確的程式碼應該是:
for i in range(1, n + 1):
total *= i
使用logging模組可以幫助我們快速地發現這種問題。
最佳實踐
在使用logging模組時,以下是一些最佳實踐:
- 使用
logging.debug()來記錄程式的執行過程 - 使用
logging.info()來記錄重要的事件 - 使用
logging.warning()來記錄可能的問題 - 使用
logging.error()來記錄錯誤 - 使用
logging.critical()來記錄嚴重的錯誤
透過遵循這些最佳實踐,你可以有效地使用logging模組來除錯和記錄你的程式。
訊息記錄層級
訊息記錄層級(Logging Levels)提供了一種方法來根據訊息的重要性對其進行分類別。Python 中有五個層級的訊息記錄,從最不重要到最重要,分別是:除錯(DEBUG)、資訊(INFO)、警告(WARNING)、錯誤(ERROR)和關鍵(CRITICAL)。
訊息記錄層級表
| 層級 | 訊息記錄函式 | 描述 |
|---|---|---|
| DEBUG | logging.debug() |
最低層級,用於記錄小細節,通常只在診斷問題時才需要。 |
| INFO | logging.info() |
用於記錄一般事件或確認程式在各個點的執行狀態。 |
| WARNING | logging.warning() |
用於指示可能導致未來問題的潛在問題,但不會立即導致程式失敗。 |
| ERROR | logging.error() |
用於記錄導致程式失敗的錯誤。 |
| CRITICAL | logging.critical() |
最高層級,用於指示導致程式完全停止執行的嚴重錯誤。 |
訊息記錄範例
import logging
# 設定基本組態
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s - %(message)s')
# 記錄除錯訊息
logging.debug('一些小細節和除錯訊息')
# 記錄一般事件
logging.info('一個事件發生了')
# 記錄警告訊息
logging.warning('可能導致未來問題的潛在問題')
# 記錄錯誤訊息
logging.error('導致程式失敗的錯誤')
# 記錄關鍵錯誤訊息
logging.critical('導致程式完全停止執行的嚴重錯誤')
內容解密:
在上述範例中,我們使用 logging.basicConfig() 函式設定基本組態,包括設定層級為 DEBUG 和格式為 '%(levelname)s - %(message)s'。然後,我們使用不同的訊息記錄函式(debug()、info()、warning()、error()、critical())記錄不同層級的訊息。
圖表翻譯:
@startuml
:設定基本組態; --> :記錄除錯訊息;
:B; --> :記錄一般事件;
:C; --> :記錄警告訊息;
:D; --> :記錄錯誤訊息;
:E; --> :記錄關鍵錯誤訊息;
@enduml
在這個流程圖中,我們展示了訊息記錄的流程,從設定基本組態開始,然後記錄不同層級的訊息,最終到達記錄關鍵錯誤訊息的階段。
使用logging模組進行錯誤追蹤
Python的logging模組是一個強大的工具,能夠幫助您追蹤和管理程式中的錯誤。它提供了五個不同的logging等級:DEBUG、INFO、WARNING、ERROR和CRITICAL。每個等級都對應著不同的錯誤嚴重程度。
logging等級
- DEBUG:此等級用於除錯目的,提供最詳細的資訊。
- INFO:此等級用於提供一般性的資訊。
- WARNING:此等級用於警告可能出現的問題。
- ERROR:此等級用於表示已經發生的錯誤。
- CRITICAL:此等級用於表示嚴重的錯誤,可能會導致程式當機。
使用logging模組
要使用logging模組,首先需要匯入它。然後,您可以設定logging等級和格式。例如:
import logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
這裡,我們設定了logging等級為INFO,並定義了格式為'%(levelname)s - %(message)s'。
訊息輸出
您可以使用不同的logging函式來輸出不同等級的訊息。例如:
logging.debug('This is a debug message.')
logging.info('This is an info message.')
logging.warning('This is a warning message.')
logging.error('This is an error message.')
logging.critical('This is a critical message.')
關閉logging
如果您不想看到某些等級的訊息,您可以使用logging.disable()函式來關閉它們。例如:
logging.disable(logging.CRITICAL)
這裡,我們關閉了CRITICAL等級的訊息。
Mu的Debugger
Mu是一個整合開發環境(IDE),它提供了一個強大的Debugger工具。Debugger允許您一步一步地執行您的程式,檢視變數的值和程式的執行流程。
啟動Debugger
要啟動Debugger,您需要在Mu中點選"Debug"按鈕。然後,Debugger會暫停您的程式的執行,並顯示當前的變數值和程式的執行流程。
Debugger按鈕
Mu的Debugger提供了幾個按鈕,允許您控制程式的執行:
- Continue:繼續執行程式,直到遇到斷點或程式結束。
- Step Over:執行下一行程式碼,如果是函式呼叫,則跳過函式內的程式碼。
- Step In:執行下一行程式碼,如果是函式呼叫,則進入函式內的程式碼。
- Step Out:執行程式碼,直到傳回當前的函式。
- Stop:停止程式的執行。
範例程式
以下是一個簡單的範例程式,示範如何使用Mu的Debugger:
print('Enter the first number to add:')
first = input()
print('Enter the second number to add:')
second = input()
您可以在Mu中建立一個新檔案,複製這個程式碼,然後啟動Debugger來追蹤它的執行流程。
程式錯誤分析與除錯
問題描述
有一個簡單的程式,要求使用者輸入三個數字,並計算這三個數字的總和。然而,程式執行後發現結果不正確。
程式碼
print('Enter the first number to add:')
first = input()
print('Enter the second number to add:')
second = input()
print('Enter the third number to add:')
third = input()
print('The sum is ' + first + second + third)
問題分析
程式使用 input() 函式讀取使用者的輸入,但是 input() 函式傳回的是字串,而不是整數。因此,當程式嘗試計算總和時,其實是在串接字串,而不是進行數字加法。
解決方案
為瞭解決這個問題,我們需要將使用者的輸入轉換為整數。可以使用 int() 函式來實作這一點。
print('Enter the first number to add:')
first = int(input())
print('Enter the second number to add:')
second = int(input())
print('Enter the third number to add:')
third = int(input())
print('The sum is', first + second + third)
除錯工具
Mu 編輯器中的除錯工具可以幫助我們找出程式中的錯誤。透過設定斷點和單步執行程式,我們可以觀察變數的值和程式的執行流程。
斷點設定
斷點是程式中的一個位置,當程式執行到該位置時,除錯工具會暫停程式的執行。這樣,我們就可以觀察變數的值和程式的執行流程。
範例:硬幣翻轉模擬
下面的程式模擬翻轉硬幣 1000 次,並計算正面的次數。
import random
heads = 0
for i in range(1, 1001):
if random.randint(0, 1) == 1:
heads = heads + 1
if i == 500:
print('Halfway done!')
透過設定斷點和單步執行程式,我們可以觀察變數 heads 的值和程式的執行流程。
圖表翻譯
@startuml
:開始; --> :輸入第一個數字;
:B; --> :輸入第二個數字;
:C; --> :輸入第三個數字;
:D; --> :計算總和;
:E; --> :輸出結果;
@enduml
此圖表展示了程式的執行流程,從輸入第一個數字到輸出結果。
程式除錯工具
程式除錯是軟體開發中的一個重要步驟,透過使用適當的工具和技術,可以快速地找出和修復程式中的錯誤。Python 提供了多種工具和技術來幫助開發者進行程式除錯,包括斷言(assert)、異常(exception)、日誌記錄(logging)和偵錯程式(debugger)。
斷言(Assert)
斷言是一種用於檢查程式中某些條件是否成立的工具,如果條件不成立,則會引發一個 AssertionError。斷言通常用於檢查程式中的邏輯是否正確,例如檢查某個變數是否為空或某個函式是否傳回了預期的結果。
assert spam >= 10, 'spam must be 10 or greater'
異常(Exception)
異常是程式中的一種錯誤處理機制,當程式出現錯誤時,可以引發一個異常,然後使用 try-except 塊來捕捉和處理異常。
try:
# 程式碼
except Exception as e:
# 處理異常
日誌記錄(Logging)
日誌記錄是一種用於記錄程式執行過程中重要事件的工具,可以幫助開發者瞭解程式的執行狀態和找出錯誤。Python 的 logging 模組提供了多種日誌記錄級別,包括 debug、info、warning、error 和 critical。
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug('This is a debug message')
偵錯程式(Debugger)
偵錯程式是一種用於一步一步地執行程式和檢查變數值的工具,可以幫助開發者瞭解程式的執行過程和找出錯誤。Python 的 pdb 模組提供了一個基本的偵錯程式,可以使用 step、next 和 continue 等命令來控制程式的執行。
import pdb
pdb.set_trace()
練習題
- 寫一個斷言陳述式,當變數 spam 小於 10 時引發 AssertionError。
- 寫一個斷言陳述式,當變數 eggs 和 bacon 的值相同(忽略大小寫)時引發 AssertionError。
- 寫一個斷言陳述式,總是引發 AssertionError。
- 要呼叫 logging.debug() 需要在程式中新增哪兩行程式碼?
- 要使 logging.debug() 將日誌訊息傳送到檔案 programLog.txt 需要在程式中新增哪兩行程式碼?
- 有哪五個日誌記錄級別?
- 可以新增哪一行程式碼來停用所有日誌訊息?
- 為什麼使用日誌訊息比使用 print() 顯示相同的訊息更好?
- Step Over、Step In 和 Step Out 按鈕在偵錯程式中的區別是什麼?
- 在點選 Continue 後,偵錯程式什麼時候會停止?
- 什麼是斷點?
- 如何在 Mu 中設定斷點?
從技術架構視角來看,Python 的函式和模組機制為程式設計提供了強大的組織和抽象能力。深入剖析函式的區域性作用域、傳回值機制以及與全域變數的互動方式,可以發現這些特性有效提升了程式碼的可讀性、可維護性和可重用性。然而,錯誤處理和除錯仍然是開發過程中不可避免的挑戰。透過 try-except 區塊、斷言以及 logging 模組的靈活運用,開發者可以有效地捕捉、處理和追蹤程式錯誤,提升程式碼的健壯性。展望未來,更進階的除錯工具和技術,例如整合式開發環境(IDE)提供的圖形化除錯介面和自動化測試框架,將進一步簡化除錯流程,提升開發效率。對於追求程式碼品質的開發者而言,持續學習和應用這些工具和技術至關重要。玄貓認為,掌握紮實的錯誤處理和除錯技巧是成為一名優秀 Python 程式設計師的必經之路。