一、 PEP 695: 類型形參語法的革新
PEP 695 在 Python 3.12 中引入了一種新穎且更為清晰的方式來定義泛型類和函數,旨在提升類型參數的明確性和簡潔性。這個提案不僅改善了類型系統的可讀性,還增強了其功能性。以下是這些變化的詳細概述:
1. 泛型類和函數的新定義方式
在 PEP 484 的基礎上,PEP 695 引入了一種更緊湊、明確的泛型類和函數的創建方法。與以前的詳細語法相比,新方法更簡潔,且類型參數的范圍更加明確。例如:
def max[T](args: Iterable[T]) -> T:...class list[T]:def __getitem__(self, index: int, /) -> T:...def append(self, element: T) -> None:...
2. 類型別名的新聲明方式
PEP 695 還引入了使用 type
語句聲明類型別名的方法。這些別名可以是普通類型或泛型類型,為類型注解提供了更多的靈活性。例如:
type Point = tuple[float, float]
type Point[T] = tuple[T, T]
3. 新的類型參數聲明
這個提案允許聲明各種新的類型參數,例如 TypeVarTuple
和 ParamSpec
,以及帶邊界或約束的 TypeVar
。這些新的類型參數擴展了 Python 類型系統的表達能力。例如:
type IntFunc[**P] = Callable[P, int] # ParamSpec
type LabeledTuple[*Ts] = tuple[str, *Ts] # TypeVarTuple
type HashableSequence[T: Hashable] = Sequence[T] # TypeVar with bound
type IntOrStrSequence[T: (int, str)] = Sequence[T] # TypeVar with constraints
4. 惰性求值和作用域
PEP 695 強調,類型別名的值和類型變量的邊界、約束僅在需要時進行求值,即實現了惰性求值。這意味著類型別名可以引用稍后在文件中定義的其他類型。同時,通過類型參數列表聲明的類型參數在其聲明的作用域內及嵌套作用域內可見,但在外部作用域不可見。
5. 標注作用域的引入
為了支持這些作用域定義,PEP 695 引入了一種新的作用域——標注作用域。這種作用域在很大程度上類似于函數作用域,但其與封閉類作用域的交互方式有所不同。在未來的 Python 3.13 中,標注也將在這種新的標注作用域中進行求值。
通過這些改進,PEP 695 不僅提高了類型注解的可讀性和易用性,還為 Python 類型系統帶來了更強的表達力和靈活性。更多細節可以在 PEP 695 中找到。
二、 PEP 701: 對 f-字符串的增強和放寬限制
Python 3.12 中的 PEP 701 帶來了對 f-字符串(格式化字符串)的顯著改進,放寬了之前的一些限制,使其更加強大和靈活。以下是這些改進的詳細介紹:
1. 引號的重用
在 Python 3.11 及之前版本中,f-字符串中不能使用與其本身相同的引號,否則會引發 SyntaxError
。例如,如果 f-字符串用單引號標記,則表達式部分不能包含單引號。這種限制在 Python 3.12 中被取消,現在你可以在 f-字符串的表達式部分自由使用任何類型的引號。例如:
songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']
f"This is the playlist: {', '.join(songs)}"
# 輸出: 'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
此外,這項改變使得 f-字符串可以更靈活地嵌套使用,如:
f"{f'{f"{f"{f"{1+1}"}"}"}'}"
# 輸出: '2'
2. 多行表達式和注釋
在以前的版本中,f-字符串的表達式必須在一行內完成,這對于復雜的表達式或需要注釋的情況不太方便。Python 3.12 允許 f-字符串表達式跨越多行,并支持在其中添加注釋,使代碼更易于理解和維護。例如:
f"""This is the playlist: {', '.join(['Take me back to Eden', # My, my, those eyes like fire'Alkaline', # Not acid nor alkaline'Ascensionism' # Take to the broken skies at last
])}"""
# 輸出: 'This is the playlist: Take me back to Eden, Alkaline, Ascensionism'
3. 反斜杠和 Unicode 字符
在 Python 3.12 之前,f-字符串表達式中不能包含任何反斜杠 (\
) 字符,這限制了 Unicode 轉義序列的使用。現在,你可以在 f-字符串表達式中使用反斜杠和 Unicode 轉義序列。例如:
songs = ['Take me back to Eden', 'Alkaline', 'Ascensionism']
print(f"This is the playlist: {'\n'.join(songs)}")
# 輸出:
# This is the playlist: Take me back to Eden
# Alkaline
# Ascensionismprint(f"This is the playlist: {'\N{BLACK HEART SUIT}'.join(songs)}")
# 輸出: 'This is the playlist: Take me back to Eden?Alkaline?Ascensionism'
總的來說,PEP 701 為 Python 開發者提供了更大的靈活性和表達能力,尤其是在處理復雜的字符串格式化時。這些改進使得 f-字符串成為一個更加強大和便捷的工具。
4. f-字符串錯誤消息的變化
在 Python 3.11 及以前版本中,f-字符串的錯誤消息往往缺乏精確性。例如,在遇到語法錯誤時,Python 3.11 可能會給出如下錯誤提示:
my_string = f"{x z y}" + f"{1 + 1}"
# 錯誤輸出:
# File "<stdin>", line 1
# (x z y)
# ^^^
# SyntaxError: f-string: invalid syntax. Perhaps you forgot a comma?
在這種情況下,錯誤消息不僅沒有準確指出錯誤的具體位置,而且錯誤的表達式被不自然地用括號括起來。
Python 3.12 使用 PEG 解析器來解析 f-字符串,使得錯誤消息變得更加精確和有用。現在,當遇到類似的語法錯誤時,錯誤消息會精確指出錯誤所在的位置,并顯示整行代碼:
my_string = f"{x z y}" + f"{1 + 1}"
# 錯誤輸出:
# File "<stdin>", line 1
# my_string = f"{x z y}" + f"{1 + 1}"
# ^^^
# SyntaxError: invalid syntax. Perhaps you forgot a comma?
四、PEP 684: 引入解釋器級 GIL 提高 Python 多核性能
PEP 684 在 Python 3.12 中引入了一個重要的更新——解釋器級的全局解釋器鎖(GIL)。這項改進旨在解決 Python 在多核 CPU 性能方面的一些限制,為 Python 帶來更好的并行計算能力。
1. 解釋器級 GIL 的概念
GIL 是 Python 解釋器中一個眾所周知的特性,它在任何時候只允許一個線程執行 Python 字節碼。雖然這簡化了 CPython 解釋器的設計并提高了單線程程序的性能,但它也限制了 Python 程序在多核處理器上的并行執行能力。
PEP 684 通過引入解釋器級 GIL,使得可以創建帶有獨立 GIL 的子解釋器。這意味著每個子解釋器可以在其自己的線程中獨立運行,從而更好地利用多核 CPU 的性能。
2. 如何創建帶有獨立 GIL 的解釋器
目前,這個特性只能通過 C-API 實現,預計在 Python 3.13 中將添加相應的 Python API。使用 Py_NewInterpreterFromConfig()
函數可以創建一個具有獨立 GIL 的新解釋器。以下是一個示例代碼:
PyInterpreterConfig config = {.check_multi_interp_extensions = 1,.gil = PyInterpreterConfig_OWN_GIL,
};
PyThreadState *tstate = NULL;
PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config);
if (PyStatus_Exception(status)) {return -1;
}
// 新解釋器現在在當前線程中激活
3. 示例和進一步的應用
更多關于如何使用 C-API 來操作子解釋器和解釋器級 GIL 的示例可以在 Modules/_xxsubinterpretersmodule.c 中找到。
總體而言,PEP 684 的引入為 Python 的并行處理和多核性能優化提供了一個重要的步驟,使 Python 在多核處理器上的應用變得更加高效。這一改進由 Eric Snow 貢獻,并在 gh-104210 等項目中得以實現。
五、 PEP 669: 為 CPython 引入低影響監控機制
PEP 669 在 Python 3.12 中引入了一個為 CPython 設計的新 API,用于實現低影響的性能監控。這一提案的核心在于為性能分析器、調試器和其他監控工具提供了一個高效且對程序性能影響小的監控方式。
1.新 API 的特點和優勢
-
廣泛的事件覆蓋:該 API 能監控包括函數調用、返回、代碼行執行、異常處理和跳轉等在內的多種事件,為開發者提供全面的性能分析視角。
-
精確的性能開銷控制:PEP 669 允許開發者只對他們需要監控的事件付出性能開銷。這種選擇性監控大大降低了對程序性能的影響,尤其是在生產環境中。
-
近零開銷的調試和覆蓋工具支持:通過這個 API,開發者可以使用幾乎不影響程序性能的調試器和代碼覆蓋工具,這在以往的 Python 版本中是難以實現的。
2. 使用 sys.monitoring 模塊
這個新引入的 sys.monitoring
模塊提供了訪問和控制事件監控的接口。通過這個模塊,開發者可以注冊監控事件、控制監控的粒度和范圍,以及收集和分析性能數據。具體使用方法和 API 文檔可以在 sys.monitoring
中找到。
六、 PEP 688: 在 Python 中使緩沖區協議更易于訪問
PEP 688 為 Python 3.12 帶來了對緩沖區協議的重要改進,使得在 Python 代碼中使用和實現緩沖區協議變得更加直接和易于管理。
1. 緩沖區協議的 Python 實現
緩沖區協議(buffer protocol)在 Python 中主要用于提供一種訪問對象內存表示的機制。這個協議常見于需要高性能數據處理的場景,比如在處理大量數據或進行科學計算時。
PEP 688 引入了以下關鍵特性:
-
__buffer__()
方法: 類通過實現__buffer__()
方法可以作為緩沖區類型使用。這允許自定義類直接支持緩沖區協議,從而與 Python 的內置緩沖區類型(如字節串和數組)一樣,可以更有效地處理數據。 -
collections.abc.Buffer
抽象基類: 這個新引入的抽象基類定義了緩沖區對象應有的基本行為和接口。通過實現這個類,開發者可以確保他們的自定義緩沖區類型符合預期的標準,從而提高代碼的可維護性和可讀性。 -
inspect.BufferFlags
枚舉: 這個新的枚舉提供了一組標志,用于自定義緩沖區的創建過程。這些標志讓開發者能夠更精確地控制緩沖區的行為,如只讀或只寫訪問,以及緩沖區的其他屬性。
2. 應用和好處
這些改進使得緩沖區協議在 Python 中更易于訪問和使用,特別是對于需要高效數據處理的應用。它們還提高了與緩沖區相關的代碼的可讀性和可維護性,使得開發者能夠更容易地實現和使用復雜的內存管理邏輯。這對于科學計算、數據分析、圖像處理等領域的 Python 開發者來說,是一個重要的步驟。
七、 Python 3.12 更新:改進的錯誤消息和類型提示特性
Python 3.12 版本中包含了對錯誤消息和類型提示的重要改進,這些更新旨在提高代碼的可讀性和易用性。
1. 改進的錯誤消息
-
更明確的
NameError
提示:- 當引發
NameError
并傳播到最高層級時,如果與標準庫中的模塊名稱相關,錯誤消息會建議可能的導入。 - 示例:
>>> sys.version_info NameError: name 'sys' is not defined. Did you forget to import 'sys'?
- 當引發
-
針對實例的
NameError
改進:- 如果在類方法中出現
NameError
,且實例具有與異常中名稱相同的屬性,則錯誤建議會包括self.<NAME>
。 - 示例:
class A:def __init__(self):self.blech = 1def foo(self):somethin = blechA().foo() # NameError: name 'blech' is not defined. Did you mean: 'self.blech'?
- 如果在類方法中出現
-
改進的
SyntaxError
:- 當用戶錯誤地使用
import x from y
語法時,會提示正確的from y import x
形式。 - 示例:
>>> import a.y.z from b.y.z SyntaxError: Did you mean to use 'from ... import ...' instead?
- 當用戶錯誤地使用
-
更有幫助的
ImportError
:- 如果嘗試從模塊導入不存在的名稱時,將會根據模塊中的可用名稱提出建議。
- 示例:
>>> from collections import chainmap ImportError: cannot import name 'chainmap' from 'collections'. Did you mean: 'ChainMap'?
2. 類型的新增特性
-
PEP 692:
TypedDict
的應用:- 允許使用
TypedDict
來更精確地注釋**kwargs
。 - 示例:
from typing import TypedDict, Unpackclass Movie(TypedDict):name: stryear: intdef foo(**kwargs: Unpack[Movie]):...
- 允許使用
-
PEP 698:
typing.override
裝飾器:- 這個新裝飾器指示方法旨在重寫超類中的方法,幫助捕獲錯誤。
- 示例:
from typing import overrideclass Base:def get_color(self) -> str:return "blue"class GoodChild(Base):@overridedef get_color(self) -> str:return "yellow"class BadChild(Base):@overridedef get_colour(self) -> str:return "red" # 'BadChild.get_colour' 引發類型檢查器錯誤,因為它沒有重寫 'Base.get_color'
這些改進不僅提升了 Python 的錯誤消息的準確性和可理解性,還增強了類型提示的表達能力,使得 Python 代碼更加健壯和易于維護。