常見的 Python 錯誤以及如何避免它們

1 濫用可變默認參數

Python 中最常見和最微妙的錯誤之一涉及在函數中使用可變默認參數。這可能會導致難以調試的意外行為。

錯誤

在定義函數時,可能想使用可變數據類型(如列表或字典)作為默認參數。下面是一個典型的示例:

def add_item(item, item_list=[]):
    item_list.append(item)
    return item_list

乍一看,這個函數似乎很簡單:它向列表中添加一個項目並返回更新的列表。但是,多次調用此函數時可能會產生意外結果:

print(add_item(1))  # Output: [1]
print(add_item(2))  # Output: [1, 2]
print(add_item(3))  # Output: [1, 2, 3]

可能希望每次調用 add_item 都會返回一個僅包含新項的列表,但相反,它會累積來自先前調用的項。

為什麼會這樣

在 Python 中,默認參數值僅在定義函數時計算一次,而不是每次調用函數時計算。這意味著,如 果使用 list 等可變對象作為默認值,則對該函數的所有調用都共享同一對象。

解決方案

要避免此問題,請使用 None 作為默認值,然後根據需要在函數內創建一個新列表:

def add_item(item, item_list=None):
    if item_list is None:
        item_list = []
    item_list.append(item)
    return item_list

現在,對 add_item 的每次調用都將按預期運行:

print(add_item(1))  # Output: [1]
print(add_item(2))  # Output: [2]
print(add_item(3))  # Output: [3]

最佳實踐

使用可變對象作為默認參數時,請始終保持謹慎。如果默認值需要可變,請考慮使用 None 並在函數內初始化對象。

2 忘記關閉文件

文件處理是 Python 中的一項常見任務,但如您在打開文件後忘記關閉文件,則可能會遇到內存泄漏或文件鎖定等問題。

錯誤

當在 Python 中打開文件時,完成後將其關閉至關重要。下面是開發人員可能忘記這樣做的示例:

file = open('example.txt', 'r')
content = file.read()
# Forgot to close the file

忘記關閉文件可能會導致問題,尤其是在處理多個文件或循環處理文件時。

為什麼會這樣

Python 會保留對文件的引用,直到它被顯式關閉或程序終止。如果忘記關閉文件,文件描述符將保持打開狀態,這可能會導致資源泄漏。

解決方案

在 Python 中處理文件的最佳方法是使用 with 語句,該語句在執行代碼塊後自動關閉文件:

with open('example.txt', 'r') as file:
    content = file.read()

這可確保文件正確關閉,即使塊中發生異常也是如此。

最佳實踐

在處理文件時,請始終使用 with 語句。它更簡潔、更具可讀性,並防止您意外地打開文件。

令人困惑的==和is

在 Python 中,==is 經常被初學者錯誤地互換使用,從而導致難以診斷的錯誤。

錯誤

a = [1, 2, 3]
b = [1, 2, 3]

print(a == b)  # Output: True
print(a is b)  # Output: False

此處,a == b 返回 True,因為 ab 的內容相等。但是,a is b 返回 False,因為 is 檢查對象標識,這意味著它會檢查 ab 是否引用內存中的同一對象。

為什麼會這樣

== 檢查兩個對象的值是否相等,而 is 檢查兩個引用是否指向同一個對象。初學者經常將兩者混淆,使用 is 是指檢查是否相等。

解決方案

為避免混淆,請記住:

  • 使用 == 比較對象的值。
  • Use 檢查兩個引用是否指向同一個對象(例如,與 None 進行比較時)。

最佳實踐

了解 ==is 之間的區別。使用 == 來比較值,用於比較對象標識,尤其是在處理字元串、整數和元組等不可變類型時。

3 誤解 Python 的範圍規則

了解 Python 的範圍規則對於避免在函數、循環和條件中處理變數時出現意外行為至關重要。

錯誤

def outer_function():
    x = 10
    def inner_function():
        print(x)
    inner_function()

outer_function()  # Output: 10

在這種情況下,inner_function可以從變數 x 的封閉outer_function訪問變數 x。但是,當嘗試修改變數時,事情會變得更加棘手:

def outer_function():
    x = 10
    def inner_function():
        x += 1
        print(x)
    inner_function()

outer_function()  # Raises UnboundLocalError

在這裡,嘗試在 inner_function 中修改 x 會引發 UnboundLocalError,因為 Python 將 x 視為 inner_function 中的局部變數(由於賦值),但它尚未在該局部範圍內初始化。

為什麼會這樣

Python 有一組定義明確的規則來解析變數,稱為 LEGB 規則:Local、Enclosing、Global 和 Built-in。當為函數中的變數賦值時,除非另有明確說明,否則 Python 會假定它是局部變數。

解決方案

要在內部函數中修改外部作用域的變數,可以使用 nonlocal 關鍵字(用於封閉作用域)或 global(用於全局作用域):

def outer_function():
    x = 10
    def inner_function():
        nonlocal x
        x += 1
        print(x)
    inner_function()

outer_function()  # Output: 11

使用 nonlocal,您可以修改封閉範圍內的變數,而不會導致錯誤。

最佳實踐

了解並遵守 Python 的範圍規則。使用 nonlocal 修改封閉範圍內的變數,使用 global 修改全局變數。避免不必要的全局變數,以降低複雜性和潛在錯誤。