讓我們一起用乾淨的程式碼迎接2025年
當你在快速原型開發時,可能會想省略乾淨的範圍設定,或重複使用常見的變數名稱(哈囉,df!),以為這樣可以節省時間。但這可能會導致隱藏的錯誤,打亂你的工作流程。
好消息是?一旦你理解了基本原則,寫出乾淨且範圍明確的程式碼並不需要額外的努力。
讓我們來分解一下。
把變數想像成一個容器,用來儲存一些資訊。範圍指的是你的程式碼中變數可以被訪問的區域。
範圍透過限制變數可以被讀取或修改的地方來防止意外的變更。如果每個變數都可以從任何地方訪問,你就必須追蹤所有變數,以避免不小心覆蓋它。
在 Python 中,範圍是由 LEGB 規則定義的,這代表:本地(Local)、封閉(Enclosing)、全域(Global)和內建(Built-in)。
讓我們用一個例子來說明。
default_tax = 0.07 # 全域範圍
def calculate_invoice(price): # 封閉範圍
discount = 0.10 total_after_discount = 0
def apply_discount(): nonlocal total_after_discount
# 本地範圍
tax = price * default_tax total_after_discount = price – (price * discount) return total_after_discount + tax
final_price = apply_discount() return final_price, total_after_discount
print(“發票總額:”, round(calculate_invoice(100)[0], 2))
1. 本地範圍
函數內的變數在本地範圍內。它們只能在該函數內被訪問。
在這個例子中,tax 是 apply_discount 函數內的一個本地變數。它在這個函數外是無法訪問的。
2. 封閉範圍
這指的是包含嵌套函數的函數中的變數。這些變數不是全域的,但可以被內部(嵌套)函數訪問。在這個例子中,discount 和 total_after_discount 是 apply_discount 的封閉範圍中的變數。
nonlocal 關鍵字:
nonlocal 關鍵字用來修改封閉範圍中的變數,而不僅僅是讀取它們。
例如,假設你想更新 total_after_discount 變數,這是在函數的封閉範圍內。如果不使用 nonlocal,當你在內部函數中對 total_after_discount 進行賦值時,Python 會將其視為一個新的本地變數,從而遮蔽外部變數。這可能會引入錯誤和意外的行為。
3. 全域範圍
在所有函數外定義的變數,可以在整個程式中訪問。
全域聲明
當你在函數內聲明一個變數為全域時,Python 將其視為對外部變數的引用。這意味著對它的更改將影響全域範圍中的變數。
使用全域關鍵字,Python 會創建一個新的本地變數。
x = 10 # 全域變數
def modify_global(): global x # 聲明 x 參考全域變數 x = 20 # 修改全域變數
modify_global() print(x) # 輸出:20。如果沒有聲明 “global”,這將顯示 10
4. 內建範圍
指的是 Python 用於其內建函數的保留關鍵字,例如 print、def、round 等。這可以在任何層級訪問。
這兩個關鍵字對於修改不同範圍中的變數至關重要,但它們的用法不同。
全域:用於修改全域範圍中的變數。非全域:用於修改封閉(非全域)範圍中的變數。
變數遮蔽發生在內部範圍中的變數隱藏了外部範圍中的變數。
在內部範圍內,對變數的所有引用都將指向內部變數,而不是外部變數。如果不小心,這可能會導致混淆和意外的輸出。
一旦執行返回到外部範圍,內部變數將不再存在,任何對變數的引用將指向外部範圍的變數。
這裡有一個快速的例子。x 在每個範圍中都被遮蔽,根據上下文產生不同的輸出。
x = 10 # 全域範圍
def outer_function(): # 封閉範圍 x = 20
def inner_function(): # 本地範圍 x = 30 print(x) # 輸出 30
inner_function() print(x) # 輸出 20
outer_function() print(x) # 輸出 10
與變數遮蔽相似的概念,但這發生在本地變數重新定義或覆蓋傳遞給函數的參數時。
def foo(x): x = 5 # 遮蔽參數 `x` return x
foo(10) # 輸出:5
x 被傳遞為 10。但它立即被 x=5 遮蔽和覆蓋。
每次遞歸調用都有自己的執行上下文,這意味著該調用中的本地變數和參數與之前的調用是獨立的。
然而,如果變數在全域上被修改或明確作為參數傳遞,則更改可能會影響後續的遞歸調用。
本地變數:這些是在函數內定義的,只影響當前的遞歸層級。它們不會在調用之間持續存在。
顯式傳遞到下一個遞歸調用的參數保留其來自前一次調用的值,允許遞歸在層級之間累積狀態。
全域變數:這些在所有遞歸層級之間共享。如果被修改,則更改將對所有遞歸層級可見。
讓我們用一個例子來說明。
例子 1:使用全域變數(不推薦)
counter = 0 # 全域變數
def count_up(n): global counter if n > 0: counter += 1 count_up(n – 1)
count_up(5) print(counter) # 輸出:5
counter 是在所有遞歸調用中共享的全域變數。它在每個遞歸層級中遞增,最終值(5)在遞歸完成後被打印出來。
例子 2:使用參數(推薦)
def count_up(n, counter=0): if n > 0: counter += 1 return count_up(n – 1, counter) return counter
result = count_up(5) print(result) # 輸出:5
counter 現在是函數的一個參數。counter 從一個遞歸層級傳遞到下一個,並在每個層級中更新其值。counter 在每次調用中不會重新初始化,而是將當前狀態傳遞給下一個遞歸層級。這個函數現在是純的——沒有副作用,並且僅在其自己的範圍內運作。當遞歸函數返回時,counter “向上冒泡”到最上層並在基礎情況下返回。
1. 使用描述性變數名稱
避免使用模糊的名稱,如 df 或 x。使用描述性名稱,如 customer_sales_df 或 sales_records_df 來提高清晰度。
2. 使用大寫字母表示常數
這是 Python 中常數的標準命名慣例。例如,MAX_RETRIES = 5。
3. 儘量避免全域變數
全域變數會引入錯誤,並使程式碼更難測試和維護。最好在函數之間明確傳遞變數。
4. 儘量寫純函數
什麼是純函數?
確定性:對於相同的輸入,它總是產生相同的輸出。它不受外部狀態或隨機性的影響。
無副作用:它不修改任何外部變數或狀態。它僅在其本地範圍內運作。
使用 nonlocal 或 global 將使函數變得不純。
然而,如果你在處理閉包時,應該使用 nonlocal 關鍵字來修改封閉(外部)範圍中的變數,這有助於防止變數遮蔽。
閉包發生在嵌套函數(內部函數)捕獲並引用來自其封閉函數(外部函數)的變數時。這使得內部函數能夠“記住”它創建時的環境,包括訪問外部函數範圍中的變數,即使外部函數已經執行完畢。
閉包的概念可以深入探討,所以如果這是我在下一篇文章中應該深入的內容,請在評論中告訴我! 🙂
5. 避免變數遮蔽和參數遮蔽
如果你需要引用外部變數,避免在內部範圍中重複使用其名稱。使用不同的名稱來清楚區分變數。
這就是全部內容!感謝你陪我到最後。
你在自己的工作中遇到過這些挑戰嗎?在下面的評論中分享你的想法吧!
我定期撰寫有關 Python、軟體開發和我所建立的專案的文章,所以請關注我,不要錯過。下篇文章見! 🙂
本文由 AI 台灣 運用 AI 技術編撰,內容僅供參考,請自行核實相關資訊。
歡迎加入我們的 AI TAIWAN 台灣人工智慧中心 FB 社團,
隨時掌握最新 AI 動態與實用資訊!