Python 中的 typing.ClassVar 詳解

一、ClassVar 的定義和基本用途

ClassVar 是 typing 模塊中提供的一種特殊類型,用于在類型注解中標記類變量(靜態變量)。根據官方文檔,使用 ClassVar[…] 注釋的屬性表示該屬性只在類層面使用,不應在實例上賦值
例如:

from typing import ClassVarclass Starship:stats: ClassVar[dict[str, int]] = {}  # 類變量damage: int = 10                     # 實例變量

上例中,stats 被標注為 ClassVar,表示它是一個共享的類級別變量;damage 則是普通的實例變量。需要注意的是,ClassVar 只是類型提示,不改變運行時行為;它本身不是類,也不能用于 isinstance() 或 issubclass() 檢查。

二、ClassVar 與實例變量的區別

在 Python 中,類中定義并賦值的變量默認屬于類屬性,所有實例共享同一份數據。使用 ClassVar 注解后,靜態類型檢查器會將該屬性視為“類變量”,并禁止通過實例來賦值。相反,實例變量通常在 init 方法中初始化,或者在類體中僅使用類型注解而不賦默認值。例如,下面的寫法會導致混淆, 不建議這樣寫:

class A:x: ClassVar[int] = 1  # 類變量y: int = 2            # 實際上,這里 y 是在類體中賦值,運行時也是類變量def __init__(self):self.y = 2        # 將 y 定義為實例變量

如上所示,不要在類體中給期望的實例變量賦值,否則該變量既被注解為實例屬性,又被賦予了類屬性的默認值,導致類型檢查和邏輯上的混淆。正確做法是:在類體中僅使用注解不賦值(y: int),并在 init 中給實例屬性賦值;或者如果需要類級別配置,則顯式使用 ClassVar 注解。類型檢查器(如 mypy)會識別 ClassVar 注釋的屬性,并在不當使用時發出警告或錯誤。

PEP 526 背景: ClassVar 的引入源自 PEP 526(2016 年提出),該 PEP 為變量注解提供了語法。PEP 526 明確指出,通過 ClassVar[…] 注解的變量標識為類變量,不應在實例上被賦值。在 PEP 526 的示例中,有如下類:

class Starship:captain: str = 'Picard'               # 實例屬性(默認值)damage: int                           # 實例屬性(無默認值)stats: ClassVar[dict[str, int]] = {}  # 類屬性

這里 stats 是真正意義上的類變量(比如記錄游戲統計數據),而 captain 只是為實例提供了一個默認值。PEP 526 解釋說,區分類變量和實例變量對靜態類型檢查器很有幫助,例如下面代碼中,如果不使用 ClassVar:

enterprise = Starship(3000)
enterprise.stats = {}   # 如果 stats 是類變量,這里將被標記為錯誤
Starship.stats = {}     # 正確,直接修改類的屬性

使用 ClassVar 讓類型檢查器能夠在類似 enterprise.stats = {} 這種賦值操作上報錯。

三、適用場景

ClassVar 主要用于需要共享或靜態存儲的類屬性場景,例如:

3.1 共享配置或常量

類中定義的配置信息、常量或緩存(如超時、默認值等),需要被所有實例共享。通過 ClassVar 標記后,這些屬性被視為類級別常量
例如:

class Config:DEFAULTS: ClassVar[dict] = {'timeout': 5, 'verbose': False}
3.2 dataclass 中排除實例字段

在使用 @dataclass 時,可以用 ClassVar 標記那些不應出現在 init 中的類屬性。ClassVar 注釋的字段不會被視為實例字段,因此不會成為構造參數。
例如:

@dataclass
class Point:x: inty: intcount: ClassVar[int] = 0  # 類級計數器,不作為實例字段

在上例中,count 不會出現在自動生成的 init 方法參數中。

3.3 類型協議

在使用結構化子類型(PEP 544 的 Protocol)時,可以用 ClassVar 區分類屬性和實例屬性,幫助類型檢查器理解協議的成員性質。RealPython 的示例也指出:“應該使用 ClassVar 來區分類屬性和實例屬性”。

3.4 其他靜態用途

如實現單例模式、緩存計算結果、計數器等場合,ClassVar 都可用于標識那些跨實例共享的數據。

四、代碼示例

from typing import ClassVarclass Starship:stats: ClassVar[dict[str, int]] = {}  # 類變量damage: int = 10                      # 實例變量enterprise = Starship()
print(Starship.stats)  # 輸出 {}
print(enterprise.stats)  # 同樣輸出 {}(實例讀取的是類屬性)
enterprise.stats = {'hits': 1}  # 通過實例賦值:會創建實例屬性,不推薦
print(Starship.stats)  # 仍輸出 {},說明類屬性未被改變
print(enterprise.stats)  # 輸出 {'hits': 1},實例屬性覆蓋了類屬性

在上述示例中,stats 被標記為 ClassVar,表明它應作為類屬性共享使用。從運行結果可以看到,通過實例 enterprise.stats = {…} 賦值實際上會新建一個實例屬性,不影響原有的類屬性;類型檢查器會將這種通過實例修改 ClassVar 的行為視為錯誤。

另一個示例演示了 dataclass 中的 ClassVar 用法:

from dataclasses import dataclass
from typing import ClassVar@dataclass
class Counter:x: inty: inttotal: ClassVar[int] = 0  # 類級計數器# 創建實例時,__init__ 只接收 x, y 兩個參數,total 不在其中
c1 = Counter(1, 2)
c2 = Counter(3, 4)
print(Counter.total, c1.total, c2.total)  # 輸出 0 0 0
Counter.total = 5
print(c1.total, c2.total)  # 輸出 5 5(所有實例共享類屬性)

在這個例子中,total 使用了 ClassVar 注解,所以在 dataclass 自動生成的構造函數中不會包含它。所有實例都共享同一個 total 值,且修改 Counter.total 會影響所有實例的讀值。

五、 ClassVar 與 @classmethod、@staticmethod 的關系

ClassVar、@classmethod 和 @staticmethod 屬于不同的概念,它們之間沒有直接關聯:
ClassVar 用于標記類屬性(變量),僅影響類型提示;它不會改變對象的綁定行為。
@classmethod 是一個裝飾器,用于定義類方法,使方法第一個參數接收類本身(通常命名為 cls),可用于訪問或修改類狀態。
@staticmethod 也是裝飾器,將方法轉為靜態方法,不接收類或實例的隱式參數,類似普通函數。
簡而言之,ClassVar 關注的是數據(屬性)級別的靜態標記,而 @classmethod/@staticmethod 是對方法的綁定方式的修飾,兩者作用域不同、互不干擾。

六、 常見誤用及陷阱

誤以為運行時生效: ClassVar 只是類型標記,對程序運行時無任何影響。不要指望它在運行時阻止屬性被修改;它不會生成新的行為或存儲方式。

在實例上賦值: 盡管運行時允許 instance.var = …,但類型檢查器會認為這是錯誤的。mypy 示例中指出,將類變量通過實例賦值會報錯(但代碼運行時依然會新建實例屬性)。正確的操作應修改類屬性:ClassName.var = …。

省略類型參數: 如果在 ClassVar 中省略類型(例如寫成 x: ClassVar = 0),這會導致該屬性被視為隱式 Any 類型。這一行為可能與預期不符,應始終提供具體類型:ClassVar[int]。

ClassVar 不是類: ClassVar 不能用于 isinstance() 或 issubclass() 等檢查;它本身也不是可實例化的類。

類型變量(TypeVar)不可用: ClassVar 的類型參數必須是具體類型,不能使用類型變量。例如 ClassVar[T](其中 T 是 TypeVar)是非法的,會被靜態檢查器視為錯誤。

與 Final 一起使用: PEP 591 建議不要同時將 ClassVar 和 Final 注解標記在同一個屬性上。Python 3.12 及更早版本中,兩者同時使用會導致錯誤;正確的做法是僅使用 Final 注解即可表示類級常量。在 Python 3.13 及以后版本中,文檔已允許 ClassVar 與 Final 嵌套使用。

濫用概念: 不要將 ClassVar 當成 Java/C++ 中那種“靜態變量”語義上的特殊對象;在 Python 中,它僅是一個類型提示工具,不會自動創建或隱藏實例屬性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83385.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83385.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83385.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

架構與UML4+1視圖

簡單對比分析 架構41視圖 架構41視圖是由Philippe Kruchten提出的,用于描述軟件系統的架構。它包括以下五個視圖: 邏輯視圖:描述系統的功能需求,展示系統的靜態結構,通常使用類圖、對象圖等。開發視圖:…

Redis 八股

目錄 數據類型 字符串: List: HASH: Set: Zset: BitMap:(這個及以下是后來新增的數據結構) HyperLogLog: GEO: Stream: 主要數據結構 …

基于協同過濾的文學推薦系統設計【源碼+文檔+部署】

基于協同過濾的文學推薦系統設計 摘要 隨著信息技術的飛速發展和文學閱讀需求的日益多樣化,構建一個高效、精準的文學推薦系統變得尤為重要。本文采用Spring Boot框架,結合協同過濾算法,設計并實現了一個基于用戶借閱行為和社交論壇互動的文學…

鴻蒙電腦:五年鑄劍開新篇,國產操作系統新引擎

出品 | 何璽 排版 | 葉媛 前不久,璽哥發布的《鴻蒙電腦,刺向壟斷的利刃,將重塑全球PC市場格局》發布后,獲得了讀者朋友的積極反饋,不少都期望鴻蒙電腦早日發布。 如今,它真來了! 5月8日&…

EWOMAIL

1、錯誤 Problem: problem with installed package selinux-policy-targeted-3.14.3-41.el8.noarch package fail2ban-server-1.0.2-3.el8.noarch requires (fail2ban-selinux if selinux-policy-targeted), but none of the providers can be installed - package fail2ban-…

qt5.14.2 opencv調用攝像頭顯示在label

ui界面添加一個Qlabel名字是默認的label 還有一個button名字是pushButton mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <opencv2/opencv.hpp> // 添加OpenCV頭文件 #include <QTimer> // 添加定…

Spring三級緩存的作用與原理詳解

在Spring框架中&#xff0c;Bean的創建過程涉及到了三級緩存機制。這個機制主要是為了提高單例模式下bean實例化和依賴注入的效率。本文將深入探討Spring中的三級緩存&#xff0c;以及其在bean生命周期中的重要作用。 首先&#xff0c;讓我們理解什么是三級緩存。Spring中的三…

IoTDB集群的一鍵啟停功能詳解

IoTDB&#xff08;Internet of Things Database&#xff09;作為一種專為物聯網設計的高性能時序數據庫&#xff0c;支持單機與分布式等多種部署模式。隨著節點數量的增加&#xff0c;手動管理集群的啟動與停止過程變得繁瑣。為了提升部署效率&#xff0c;IoTDB 提供了一鍵啟停…

Oracle學習日記--Oracle中使用單個inert語句實現插入多行記錄

目錄 前言&#xff1a; 問題現象&#xff1a; 問題分析&#xff1a; 解決方法&#xff1a; 1、insert into ... union all句式 2、insert all into ...select 1 from dual句式 總結&#xff1a; 前言&#xff1a; 最近項目中使用到了Oracle數據庫&#xff0c;由于Oracle數…

LabVIEW 程序運行時內存不足報錯原因

在 LabVIEW 程序開發與運行過程中&#xff0c;內存不足報錯并退出是常見且棘手的問題。這不僅影響程序穩定性&#xff0c;還可能導致數據丟失與系統崩潰。以下從程序設計、硬件資源、系統環境等多維度深入剖析其成因&#xff0c;幫助開發者準確定位并解決問題。 ? 一、程序設…

【GAN網絡入門系列】一,手寫字MINST圖片生成

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 博主簡介&#xff1a;努力學習的22級本科生一枚 &#x1f31f;?&#xff1b;探索AI算法&#xff0c;C&#xff0c;go語言的世界&#xff1b;在迷茫中尋找光芒…

Baklib加速企業AI數據智理轉型

Baklib智理AI數據資產 在AI技術深度滲透業務場景的背景下&#xff0c;Baklib通過構建企業級知識中臺架構&#xff0c;重塑了數據資產的治理范式。該平臺采用智能分類引擎與語義分析模型&#xff0c;將分散在郵件、文檔、數據庫中的非結構化數據轉化為標準化的知識單元&#xf…

如何在Windows右鍵新建菜單中添加自定義項,將notepad添加到新建菜單

一、簡介 Windows 右鍵新建菜單的核心管理機制隱藏在注冊表的 HKEY_CLASSES_ROOT 根鍵中。這里存在兩種關鍵注冊表項&#xff1a;文件擴展名項和文件類型項&#xff0c;它們共同構成了新建菜單的完整控制體系。 以常見的.txt文件為例&#xff0c;系統通過以下機制實現新建菜單…

中大型水閘安全監測系統建設實施方案

一、方案背景 隨著科技的不斷進步&#xff0c;水利工程的數字化轉型已經成為提升城市水資源管理效率和增強防洪能力的關鍵。今天&#xff0c;我們將引導您深入了解我國大中型水閘安全監測管理系統的構建方案&#xff0c;探討如何運用先進技術確保國家水安全&#xff0c;提升水利…

Gartner《如何有效融合Data Fabric 與Data Mesh數據戰略》學習心得

在當今數字化時代,數據已成為企業最為重要的戰略資產之一。企業對于高效的數據管理架構的需求日益迫切,以確保能夠從海量數據中提取有價值的信息,支持業務決策和創新。近年來,數據編織(Data Fabric)和數據網格(Data Mesh)成為了數據管理領域的兩個熱門概念,在行業內引…

matlab建立整車模型,求汽車的平順性

在MATLAB中建立整車模型評估汽車平順性&#xff0c;通常采用多自由度振動模型。以下是基于四分之一車模型的詳細步驟和代碼示例&#xff0c;可擴展至整車模型。 1. 四分之一車模型&#xff08;簡化版&#xff09; 模型描述 自由度&#xff1a;2個&#xff08;車身垂直位移 z2…

探究電阻分壓的帶負載能力

我們經常使用兩個電阻去分壓來獲得特定的電壓,那么我是兩個大阻值電阻分壓獲得的電壓驅動能力強,還是小阻值電阻分壓得到的電壓驅動能力強呢? 一、電壓相同時,電流的大小 下面是兩個阻值分壓得到的仿真圖 電路分析: VCC都是5V,探針1和探針2測到的電壓都是1.67V; 根據…

牛客網NC22222:超半的數

牛客網NC22222:超半的數 題目描述 輸入輸出格式 輸入格式&#xff1a; 第一行包含一個整數 n (1 ≤ n ≤ 1000)第二行包含 n 個整數 a_i (1 ≤ a_i ≤ 10^9) 輸出格式&#xff1a; 輸出一個整數&#xff0c;表示出現次數超過一半的那個數 解題思路 這道題目有多種解法&a…

開發日常中的抓包工具經驗談:Charles 抓包工具與其它選項對比

開發日常中的抓包工具經驗談&#xff1a;HTTPS調試怎么選&#xff1f; 在移動開發或Web API聯調時&#xff0c;網絡請求常常成為問題定位的第一難題。尤其是面對加密的 HTTPS 請求&#xff0c;傳統瀏覽器調試工具已顯得力不從心。 我們團隊最近在排查一個安卓應用中的支付延遲…

哈希表實現(1):

1. 哈希&#xff1a; 之前我們的紅黑數的查找是由于左邊小右邊大的原則可以快速的查找&#xff0c;我們這里的哈希表呢&#xff1f; 這里是用過哈希函數把關鍵字key和存儲位置建立一個關聯的映射。 直接定址法&#xff08;函數函數定義的其中一種&#xff09;&#xff1a; 直…