Following system colour scheme - Python 增強提案 Selected dark colour scheme - Python 增強提案 Selected light colour scheme - Python 增強提案

Python 增強提案 (Python Enhancement Proposals)

PEP 310 – 可靠的獲取/釋放對

作者:
Michael Hudson <mwh at python.net>, Paul Moore <p.f.moore at gmail.com>
狀態:
已否決 (Rejected)
類型:
標準軌跡 (Standards Track)
建立日期:
2002 年 12 月 18 日
Python 版本:
2.4
公告歷史:


目錄

摘要

若能有一種輸入負擔較輕的方式來撰寫程式,將會非常有幫助

the_lock.acquire()
try:
    ....
finally:
    the_lock.release()

本 PEP 提議了一種語法(‘with’ 代碼塊)和一個將上述內容泛用的「小 i」介面。

宣告

本 PEP 已被否決,取而代之的是 PEP 343

原理

Python 異常處理哲學的優點之一,在於它增加了執行「錯誤」操作(例如:忘記檢查某些系統調用的返回值)的難度。目前,這並不適用於資源清理。目前獲取和釋放資源(例如:鎖)的語法為

the_lock.acquire()
try:
    ....
finally:
    the_lock.release()

這種語法透過一段(可能很長的)代碼塊將獲取和釋放操作分隔開來,這使得很難「一眼看出」代碼是否正確地管理了資源。另一個常見的錯誤是在 try 塊中編寫「acquire」調用,這會導致如果獲取失敗時卻錯誤地釋放了鎖。

基本語法與語義

‘with’ 語句的語法如下

'with' [ var '=' ] expr ':'
    suite

此語句被定義為等同於以下語句序列

var = expr

if hasattr(var, "__enter__"):
    var.__enter__()

try:
    suite

finally:
    var.__exit__()

__exit__ 方法的存在並不__enter__ 那樣預先檢查,以確保在 with 語句中使用不適當的物件會產生錯誤)。

如果省略了變數,則會在堆疊上分配一個匿名物件。在這種情況下,套件組(suite)無法訪問該匿名物件。

可能的擴展

Python 開發者清單中討論了一些對基本語法的潛在擴展。本 PEP 提出的解決方案中均未包含這些擴展。在許多情況下,兩方的論點幾乎一樣強烈。在這種情況下,本 PEP 始終選擇簡單性,原因很簡單:如果需要額外的功能,現有的 try 塊即可使用。

多個表達式

其中一個提議是允許在單個 ‘with’ 語句中使用多個表達式。__enter__ 方法將由左至右調用,而 __exit__ 方法則由右至左。這樣做的優點是當需要管理多個資源時,嵌套的 ‘with’ 語句不會導致代碼向右側邊界漂移。解決這個問題的方法與任何其他深層嵌套相同——將部分代碼重構成獨立的函數。此外,還需要解決如果其中一個 __exit__ 方法拋出異常時會發生什麼事(是否應該調用其他的 __exit__ 方法?)。

異常處理

有人建議將協議擴展為包含可選的 __except__ 處理程序,該處理程序在拋出異常時被調用,且可以處理或重新拋出異常。目前還不清楚該擴展的語義是否能變得精確且易於理解。例如,如果定義了異常處理程序,等效代碼是否應為 try ... except ... else,如果沒有則為 try ... finally?一般來說,如何在編譯時確定這一點?另一種選擇是將代碼定義為展開為嵌套在 try ... finally 內的 try ... except。但在實際情況中這可能並不是正確的做法。

異常處理唯一確定的使用案例是交易處理(正常結束時 commit,異常時 rollback)。這對於傳統的 try ... except ... else 代碼塊來說可能同樣容易處理,因此本 PEP 不包含對異常處理程序的任何支援。

實作筆記

在被指定為與 with 語句等效的代碼中存在潛在的競爭條件。例如,如果在 __enter__ 方法調用完成與 try 塊開始之間引發了 KeyboardInterrupt 異常,則 __exit__ 方法將不會被調用。這可能導致資源洩漏或死鎖。[XXX Guido 已表示他關心這類競爭條件,並打算寫一些 C 魔法來處理它們。‘with’ 語句的實作應參考這一點。]

待決問題

現有的類別(例如:文件類物件和鎖)是否應該獲得相應的 __enter____exit__ 方法?支持的明顯原因是便利性(不需要轉接器)。反對的理由是,如果內置文件具有此功能但(假設)StringIO 沒有,那麼在文件物件上使用「with」的代碼就無法重複用於 StringIO 物件。因此,__exit__ = close 成為了「文件類物件」協議的一部分,使用者定義的類別可能需要支援此協議。

__enter__ 鉤子可能是不必要的——對於許多使用案例,需要一個轉接器類別,在那種情況下,由 __enter__ 鉤子完成的工作可以同樣輕鬆地在 __init__ 鉤子中完成。

如果有明確控制物件生命週期的方法,__exit__ 鉤子的功能可以由現有的 __del__ 鉤子接管。與該方法支持者的一系列電子郵件交流 [1] 讓其中一位作者更加確信這不是個好主意……

有人建議 [2] 將「__exit__」方法命名為「close」,或者如果找不到 __exit__ 方法,則應考慮使用「close」方法,以增加 「with ...」結構的「開箱即用實用性」。

‘with …’ 代碼塊與產生器(generators)在概念上有某些相似之處,這導致有人提議 for 循環可以實作 with 代碼塊的功能 [3]。雖然在某些層面上很巧妙,但我們認為 for 循環應該堅持作為循環。

替代方案構想

IEXEC:Holger Krekel – 使用類 XML 語法的通用方法(未找到 URL……)。

Holger 對「執行監視器」有更深遠的想法,這些監視器會被通知有關受監視區塊中控制流程的細節。雖然很有趣,但這些想法可能會以深層且微妙的方式改變語言,因此屬於不同的 PEP。

任何 Smalltalk/Ruby 匿名塊風格的擴展顯然都包含了這一個。

PEP 319 位於同一領域,但在 python-dev 上發表時並未獲得支持。

回溯相容性

本 PEP 提議了一個新的關鍵字,因此需要進行 __future__ 的過渡流程。

採納成本

那些聲稱語言變得越來越龐大且複雜的人又有東西可以抱怨了。這是另一個需要教導的內容。

為了使該提案發揮作用,標準庫中的許多文件類和鎖類類別以及其他代碼必須具有

__exit__ = close

或類似的添加內容。

不採納的成本

編寫正確的代碼仍然比編寫錯誤的代碼需要付出更多努力。

參考文獻

這裡可以提到各種 python-list 和 python-dev 討論。


來源:https://github.com/python/peps/blob/main/peps/pep-0310.rst

最後修改時間:2025-02-01 08:59:27 GMT