当前位置: 首页 > article >正文

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 中,它仅是一个类型提示工具,不会自动创建或隐藏实例属性。

相关文章:

Python 中的 typing.ClassVar 详解

一、ClassVar 的定义和基本用途 ClassVar 是 typing 模块中提供的一种特殊类型,用于在类型注解中标记类变量(静态变量)。根据官方文档,使用 ClassVar[…] 注释的属性表示该属性只在类层面使用,不应在实例上赋值 例如&…...

【动态导通电阻】GaN HEMT动态导通电阻的精确测量

2023 年 7 月,瑞士洛桑联邦理工学院的 Hongkeng Zhu 和 Elison Matioli 在《IEEE Transactions on Power Electronics》期刊发表了题为《Accurate Measurement of Dynamic ON-Resistance in GaN Transistors at Steady-State》的文章,基于提出的稳态测量方法,研究了氮化镓(…...

java 使用zxing生成条形码(可自定义文字位置、边框样式)

最新工作中遇到生成条形码的需求&#xff0c;经过一番摸索之后找到了zxing这个工具类&#xff0c;实现效果如下&#xff1a; 首先引入依赖&#xff1a; <!-- 条形码生成器 --><dependency><groupId>com.google.zxing</groupId><artifactId&g…...

day19-线性表(顺序表)(链表I)

一、补充 安装软件命令&#xff1a; sudo apt-get install (软件名) 安装格式化对齐&#xff1a;sudo apt-get install clang-format内存泄漏检测工具&#xff1a; sudo apt-get install valgrind 编译后&#xff0c;使用命令 valgrind ./a.out 即可看内存是…...

CSS- 2.1 实战之图文混排、表格、表单、学校官网一级导航栏

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在HBuilder中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 HTML系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查看&#xff01; 点…...

Armijo rule

非精线搜索步长规则Armijo规则&Goldstein规则&Wolfe规则_armijo rule-CSDN博客 [原创]用“人话”解释不精确线搜索中的Armijo-Goldstein准则及Wolfe-Powell准则 – 编码无悔 / Intent & Focused...

从零搭建AI工作站:Gemma3大模型本地部署+WebUI配置全套方案

文章目录 前言1. 安装Ollama2.Gemma3模型安装与运行3. 安装Open WebUI图形化界面3.1 Open WebUI安装运行3.2 添加模型3.3 多模态测试 4. 安装内网穿透工具5. 配置固定公网地址总结 前言 如今各家的AI大模型厮杀得如火如荼&#xff0c;每天都有新的突破。今天我要给大家安利一款…...

贝叶斯优化Transformer融合支持向量机多变量时间序列预测,Matlab实现

贝叶斯优化Transformer融合支持向量机多变量时间序列预测&#xff0c;Matlab实现 目录 贝叶斯优化Transformer融合支持向量机多变量时间序列预测&#xff0c;Matlab实现效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.BO-TransformerSVM多变量时间序列预测&#xff0c…...

执行apt-get update 报错ModuleNotFoundError: No module named ‘apt_pkg‘的解决方案汇总

Ubuntu版本ubuntu18.04 报错内容&#xff1a; //执行apt-get upgrade报错&#xff1a; Traceback :File “/usr/lib/cnf-update-db”, line 8, in <module>from CommandNotFound.db.creator import DbcreatorFile “/usr/lib/python3/dist-packages/CommandNotFound/db…...

maven中relativepath标签的含义及使用方法

在Maven中,<relativePath>标签用于指定子模块的父POM文件的相对路径,以便在构建时优先从本地项目结构中查找父项目,而非直接从仓库获取。以下是其含义和使用方法的详细说明: 含义 作用:在子模块的<parent>元素中,<relativePath>定义了父POM文件相对于当…...

C++_STL_map与set

1. 关联式容器 在初阶阶段&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、 forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面 存储的是元素本身。那什么是…...

项目依赖版本修改

React项目 因UI库无法兼容React19版本,故此降低React版本至18.x (为什么不升级UI库版本,因为没有最新版,而且找不到好的替代品) package.json 先修改package.json文件中你想修改的依赖版本号 "dependencies": { - "react": "^19.1.0", - "…...

蚁群算法赋能生鲜配送:MATLAB 实现多约束路径优化

在生鲜农产品配送中&#xff0c;如何平衡运输效率与成本控制始终是行业难题。本文聚焦多目标路径优化&#xff0c;通过 MATLAB 实现蚁群算法&#xff0c;解决包含载重限制、时间窗约束、冷藏货损成本的复杂配送问题。代码完整复现了从数据生成到路径优化的全流程&#xff0c;助…...

机器学习与人工智能:NLP分词与文本相似度分析

自然语言处理 你有没有想过&#xff0c;生成式 AI 工具或大型语言模型背后究竟发生了什么&#xff1f;自然语言处理&#xff08;NLP&#xff09;是这些工具的核心&#xff0c;它使计算机能够理解人类语言。换句话说&#xff0c;NLP 是连接人类交流和机器&#xff08;如计算机&…...

记录一下seata后端数据库由mariadb10切换到mysql8遇到的SQLException问题

文章目录 前言一、问题记录二、参考帖子三、记录store.db.driverClassName 前言 记录一下seata后端数据库由mariadb10切换到mysql8遇到的SQLException问题。 一、问题记录 17:39:23.709 ERROR --- [ionPool-Create-1134013833] com.alibaba.druid.pool.DruidDataSource : …...

CUDA学习笔记

CUDA入门笔记 总览 CUDA是NVIDIA公司对其GPU产品提供的一个编程模型&#xff0c;在2006年提出&#xff0c;近年随着深度学习的广泛应用&#xff0c;CUDA已成为针对加速深度学习算法的并行计算工具。 以下是维基百科的定义&#xff1a;一种专有的并行计算平台和应用程序编程接…...

Python爬虫实战:研究JavaScript压缩方法实现逆向解密

一、引言 在数字化信息爆炸的时代,网络数据已成为驱动各行业发展的核心资产。Python 凭借其丰富的库生态和简洁的语法,成为网络爬虫开发的首选语言。然而,随着互联网安全防护机制的不断升级,网站普遍采用 JavaScript 压缩与混淆技术保护其核心逻辑和数据传输,这使得传统爬…...

【Linux】Shell脚本中向文件中写日志,以及日志文件大小、数量管理

1、写日志 shell脚本中使用echo命令,将字符串输入到文件中 覆盖写入:echo “Hello, World!” > laoer.log ,如果文件不存在,则会创建文件追加写入:echo “Hello, World!” >> laoer.log转移字符:echo -e “Name:\tlaoer\nAge:\t18” > laoer.log,\t制表符 …...

c++ 类的语法3

测试下默认构造函数。demo1&#xff1a; void testClass3() {class Demo { // 没显示提供默认构造函数&#xff0c;会有默认构造函数。public:int x; // 普通成员变量&#xff0c;可默认构造};Demo demo1;//cout << "demo1.x: " << demo1.x << en…...

Rust 学习笔记:关于 String 的练习题

Rust 学习笔记&#xff1a;关于 String 的练习题 Rust 学习笔记&#xff1a;关于 String 的练习题选出描述正确的那一个。该程序最多可能发生多少次堆的内存分配&#xff1f;哪种说法最能解释为什么 Rust 不允许字符串索引&#xff1f;哪种说法最能描述字符串切片 &str 和字…...

Spring bean 的生命周期、注入方式和作用域

一、Spring Bean的生命周期 Spring Bean的生命周期是指从Bean的定义加载到最终销毁的整个过程&#xff0c;Spring框架在每个阶段都提供了钩子方法&#xff0c;允许开发者在特定时机执行自定义逻辑。 1. Bean定义加载阶段 容器启动时加载配置(XML/注解/JavaConfig)&#xff0…...

Python爬虫(26)Python爬虫高阶:Scrapy+Selenium分布式动态爬虫架构实践

目录 一、背景&#xff1a;动态爬虫的工程化挑战二、技术架构设计1. 系统架构图2. 核心组件交互 三、环境准备与项目搭建1. 安装依赖库2. 项目结构 四、核心模块实现1. Selenium集成到Scrapy&#xff08;中间件开发&#xff09;2. 分布式配置&#xff08;settings.py&#xff0…...

Python 之类型注解

类型注解允许开发者显式地声明变量、函数参数和返回值的类型。但是加不加注解对于程序的运行没任何影响&#xff08;是非强制的&#xff0c;且类型注解不影响运行时行为&#xff09;&#xff0c;属于 有了挺好&#xff0c;没有也行。但是大型项目按照规范添加注解的话&#xff…...

【linux】Web服务—搭建nginx+ssl的加密认证web服务器

准备工作 步骤&#xff1a; 一、 新建存储网站数据文件的目录 二、创建一个该目录下的默认页面&#xff0c;index.html 三、使用算法进行加密 四、制作证书 五、编辑配置文件&#xff0c;可以选择修改主配置文件&#xff0c;但是不建议 原因如下&#xff1a; 自定义一个配置文…...

基于HTTP头部字段的SQL注入:SQLi-labs第17-20关

前置知识&#xff1a;HTTP头部介绍 HTTP&#xff08;超文本传输协议&#xff09;头部&#xff08;Headers&#xff09;是客户端和服务器在通信时传递的元数据&#xff0c;用于控制请求和响应的行为、传递附加信息或定义内容类型等。它们分为请求头&#xff08;Request Headers&…...

实战解析MCP-使用本地的Qwen-2.5模型-AI协议的未来?

文章目录 目录 文章目录 前言 一、MCP是什么&#xff1f; 1.1MCP定义 1.2工作原理 二、为什么要MCP&#xff1f; 2.1 打破碎片化的困局 2.2 实时双向通信&#xff0c;提升交互效率 2.3 提高安全性与数据隐私保护 三、MCP 与 LangChain 的区别 3.1 目标定位不同 3.…...

SRS流媒体服务器(5)源码分析之RTMP握手

1.概述 学习 RTMP 握手逻辑前&#xff0c;需明确两个核心问题&#xff1a; rtmp协议连接流程阶段rtmp简单握手和复杂握手区别 具体可以学习往期博客&#xff1a; RTMP协议分析_rtmp与264的关系-CSDN博客 2.rtmp握手源码分析 2.1 握手入口 根据SRS流媒体服务器(4)可知&am…...

内核性能测试(60s不丢包性能)

以xGAP-200-SE7K-L&#xff08;双口10G&#xff09;在飞腾D2000上为例&#xff08;单通道最高性能约2.8Gbps) 单口测试 0口&#xff1a; tcp&#xff1a; taskset -c 4 iperf -c 1.1.1.1 -i 1 -t 60 -p 60001 taskset -c 4 iperf -s -i 1 -p 60001 udp&#xff1a; taskse…...

RabbitMQ高级篇-MQ的可靠性

目录 MQ的可靠性 1.如何设置数据持久化 1.1.交换机持久化 1.2.队列持久化 1.3.消息持久化 2.消息持久化 队列持久化&#xff1a; 消息持久化&#xff1a; 3.非消息持久化 非持久化队列&#xff1a; 非持久化消息&#xff1a; 4.消息的存储机制 4.1持久化消息&…...

MySQL 数据库集群部署、性能优化及高可用架构设计

MySQL 数据库集群部署、性能优化及高可用架构设计 集群部署方案 1. 主从复制架构 传统主从复制&#xff1a;配置一个主库(Master)和多个从库(Slave)GTID复制&#xff1a;基于全局事务标识符的复制&#xff0c;简化故障转移半同步复制&#xff1a;确保至少一个从库接收到数据…...