全面解析 Python 访问类变量与实例变量的步骤
zhezhongyun 2025-10-19 15:34 27 浏览
我会遵循以下结构为你讲解:
- 核心定义与区别:首先明确什么是类变量,什么是实例变量。
- 访问步骤详解:这是核心部分,我们将从创建到访问,一步步拆解。
- 查找顺序(重点):解释 Python 如何决定在访问变量时,是使用实例变量还是类变量。
- 修改与覆盖:讲解如何修改它们,以及一个常见的 “陷阱”。
- 使用场景与最佳实践:总结何时应该使用哪种变量。
- 总结与代码示例:用一个完整的例子回顾所有知识点。
1. 核心定义与区别
特性 | 类变量 (Class Variable) | 实例变量 (Instance Variable) |
定义位置 | 在类的内部,但在所有方法的外部。 | 在类的方法内部,通常是在 __init__ 方法中,通过 self 关键字定义。 |
所属对象 | 属于类本身。 | 属于类的具体实例(即通过类创建的对象)。 |
共享性 | 所有类的实例共享同一个类变量。 | 每个实例都有自己独立的实例变量副本。 |
生命周期 | 随类的定义而创建,随类的销毁而销毁。 | 随实例的创建而创建,随实例的销毁而销毁。 |
作用 | 存储所有实例共有的属性。 | 存储每个实例独有的属性。 |
简单比喻:
- 类变量:就像一个班级的班级名(例如 "高三 (1) 班")。这个名字属于整个班级,是所有同学共有的。
- 实例变量:就像班级里每个同学的姓名(例如 "张三", "李四")。每个同学都有自己的名字,是独一无二的。
2. 访问步骤详解
我们通过一个完整的例子来分解访问的步骤。
python
运行
class Dog:
# 1. 定义类变量
species = "Canis lupus familiaris" # 所有狗都属于这个物种
def __init__(self, name, age):
# 2. 定义实例变量
self.name = name # 每只狗有自己的名字
self.age = age # 每只狗有自己的年龄
# 3. 创建实例
dog1 = Dog("Buddy", 3)
dog2 = Dog("Lucky", 5)
步骤一:定义类变量
当 Python 解释器执行到 class Dog: 代码块时,它会首先创建 Dog 这个类对象。在类体内部定义的变量,如 species,就被绑定为这个类对象的属性。此时,species 就是一个类变量。
你可以通过 Dog.species 直接访问它。
python
运行
print(f"通过类访问类变量: {Dog.species}") # 输出: Canis lupus familiaris
步骤二:创建实例并定义实例变量
当你执行 dog1 = Dog("Buddy", 3) 时,会发生以下事情:
- Python 创建一个新的、空的 Dog 实例对象。
- 它调用这个新实例的 __init__ 方法,并将实例本身作为第一个参数 self 传入。
- 在 __init__ 方法内部,self.name = name 这行代码执行。它会在这个具体的实例对象 (dog1) 上创建一个名为 name 的属性,并赋值为 "Buddy"。self.age = age 同理。
此时,dog1 实例拥有了自己的 name 和 age 属性。
步骤三:访问变量
访问变量主要有两种途径:通过实例访问 和 通过类访问。
情况 A:通过实例访问
你可以通过实例来访问实例变量和类变量。
python
运行
# 访问实例变量
print(f"Dog1的名字: {dog1.name}") # 输出: Buddy
# 访问类变量 (Python 会在实例上找不到时,去它的类上找)
print(f"Dog1的物种: {dog1.species}") # 输出: Canis lupus familiaris
情况 B:通过类访问
你可以通过类来访问类变量,但不能通过类来访问一个属于特定实例的实例变量。
python
运行
# 访问类变量
print(f"通过类访问物种: {Dog.species}") # 输出: Canis lupus familiaris
# 尝试通过类访问实例变量 (会报错)
try:
print(Dog.name)
except AttributeError as e:
print(f"错误: {e}") # 输出: 错误: type object 'Dog' has no attribute 'name'
为什么会报错? 因为 name 是属于 dog1 或 dog2 这些具体实例的,而 Dog 这个类本身并没有 name 这个属性。
3. 查找顺序(重点:“先实例,后类”)
当你通过一个实例(如 dog1)来访问一个属性(如 some_attribute)时,Python 的查找顺序是:
- 首先,在该实例自身的属性字典中查找 (dog1.__dict__)。
- 如果没找到,则去该实例所属的类的属性字典中查找 (Dog.__dict__)。
- 如果还没找到,则去该类的父类中查找(这涉及到继承,这里暂不展开)。
- 如果最终都没找到,则抛出 AttributeError 异常。
这个顺序可以总结为:先找自己,再找 “祖宗”(类)。
4. 修改与覆盖
这是最容易混淆的地方。
修改实例变量
通过实例修改实例变量,只会影响该实例本身。
python
运行
dog1.age = 4 # 修改 dog1 的 age
print(f"Dog1的新年龄: {dog1.age}") # 输出: 4
print(f"Dog2的年龄: {dog2.age}") # 输出: 5 (不受影响)
修改类变量
修改类变量有两种方式,效果完全不同。
方式一:通过类来修改 (推荐)
这种方式会真正地修改类变量的值,从而影响到所有实例。
python
运行
Dog.species = "Canis lupus" # 通过类修改 species
print(f"Dog1的新物种: {dog1.species}") # 输出: Canis lupus
print(f"Dog2的新物种: {dog2.species}") # 输出: Canis lupus
方式二:通过实例来 “修改” (这是一个陷阱!)
这种方式不会修改类变量,而是会在该实例上创建一个同名的实例变量,从而 **“覆盖”**(shadowing)掉类变量。
python
运行
# 回到最初的状态
Dog.species = "Canis lupus familiaris"
dog1 = Dog("Buddy", 3)
dog2 = Dog("Lucky", 5)
print(f"修改前, dog1.species: {dog1.species}") # Canis lupus familiaris
print(f"修改前, dog2.species: {dog2.species}") # Canis lupus familiaris
# 通过实例 "修改" 类变量
dog1.species = "Wolf"
print(f"修改后, dog1.species: {dog1.species}") # Wolf (访问的是 dog1 的实例变量)
print(f"修改后, dog2.species: {dog2.species}") # Canis lupus familiaris (访问的是 Dog 的类变量)
print(f"修改后, Dog.species: {Dog.species}") # Canis lupus familiaris (类变量根本没变)
# 查看 dog1 的属性字典,你会发现 species 已经成了它自己的实例变量
print(dog1.__dict__) # 输出: {'name': 'Buddy', 'age': 3, 'species': 'Wolf'}
为什么会这样? 因为 dog1.species = "Wolf" 这行代码遵循了赋值的逻辑:它总是尝试在 ** 左操作数(这里是 dog1)** 上创建或更新属性。所以它并没有去修改 Dog 类,而是给 dog1 实例增加了一个新的 species 属性。之后再通过 dog1.species 访问时,根据查找顺序,会直接找到实例上的 species,而不会再去类里找了。
5. 使用场景与最佳实践
- 使用类变量的场景:当你需要一个值被所有实例共享时。例如,配置信息、计数器、常量等。定义类的默认行为或属性。
- 使用实例变量的场景:当你需要每个实例都有自己独立的数据时。这是最常见的情况,比如用户的 ID、名字、银行账户余额等。
- 最佳实践:明确意图:如果你想修改所有实例共享的那个值,请务必通过类名(如 Dog.species = ...)来修改。避免混淆:尽量不要在实例上创建与类变量同名的属性,除非你非常清楚自己在做什么(例如,你确实想为某个特定实例提供一个特殊的值来覆盖默认的类行为)。使用 @classmethod 或 @staticmethod:如果一个方法需要修改类变量,通常将其定义为类方法 (@classmethod),这样更清晰,也更符合面向对象的设计原则。
6. 总结与完整代码示例
为了让你有一个清晰的全景图,这里是一个总结性的代码示例:
python
运行
class Dog:
# --- 类变量 ---
# 所有狗共享的属性
species = "Canis lupus familiaris"
# 一个计数器,记录总共创建了多少只狗
num_of_dogs = 0
def __init__(self, name, age):
# --- 实例变量 ---
# 每只狗独有的属性
self.name = name
self.age = age
# 在初始化时,通过类名修改类变量
Dog.num_of_dogs += 1
# 一个实例方法,访问实例变量和类变量
def description(self):
return f"{self.name} is a {Dog.species} and is {self.age} years old."
# --- 访问与修改 ---
# 1. 通过类访问类变量
print(f"通过类访问物种: {Dog.species}") # 输出: Canis lupus familiaris
print(f"初始狗的数量: {Dog.num_of_dogs}") # 输出: 0
# 2. 创建实例
dog1 = Dog("Buddy", 3)
dog2 = Dog("Lucky", 5)
# 3. 通过实例访问实例变量和类变量
print(f"\nDog1的名字: {dog1.name}") # 输出: Buddy
print(f"Dog1的物种: {dog1.species}") # 输出: Canis lupus familiaris (从类中找到)
# 4. 查看类变量的变化
print(f"当前狗的数量: {Dog.num_of_dogs}") # 输出: 2
# 5. 通过类修改类变量 (影响所有实例)
Dog.species = "Dog"
print(f"\n修改类变量后, Dog1的物种: {dog1.species}") # 输出: Dog
print(f"修改类变量后, Dog2的物种: {dog2.species}") # 输出: Dog
# 6. 通过实例“覆盖”类变量 (只影响当前实例)
dog1.species = "Super Dog"
print(f"\nDog1覆盖后, 它的物种: {dog1.species}") # 输出: Super Dog
print(f"Dog1覆盖后, Dog2的物种: {dog2.species}") # 输出: Dog (不受影响)
print(f"Dog1覆盖后, 类的物种: {Dog.species}") # 输出: Dog (类变量没变)
# 7. 调用实例方法
print(f"\nDog1的描述: {dog1.description()}") # 输出: Buddy is a Dog and is 3 years old.
希望这个全面的解析能帮助你彻底理解 Python 中类变量和实例变量的访问机制!
weibo.com/ttarticle/p/ShOw?id=2309405222511481847815
weibo.com/ttarticle/p/ShOw?id=2309405222533833293991
weibo.com/ttarticle/p/ShOw?id=2309405222507380081178
weibo.com/ttarticle/p/ShOw?id=2309405222518347923463
相关推荐
- Python入门学习记录之一:变量_python怎么用变量
-
写这个,主要是对自己学习python知识的一个总结,也是加深自己的印象。变量(英文:variable),也叫标识符。在python中,变量的命名规则有以下三点:>变量名只能包含字母、数字和下划线...
- python变量命名规则——来自小白的总结
-
python是一个动态编译类编程语言,所以程序在运行前不需要如C语言的先行编译动作,因此也只有在程序运行过程中才能发现程序的问题。基于此,python的变量就有一定的命名规范。python作为当前热门...
- Python入门学习教程:第 2 章 变量与数据类型
-
2.1什么是变量?在编程中,变量就像一个存放数据的容器,它可以存储各种信息,并且这些信息可以被读取和修改。想象一下,变量就如同我们生活中的盒子,你可以把东西放进去,也可以随时拿出来看看,甚至可以换成...
- 绘制学术论文中的“三线表”具体指导
-
在科研过程中,大家用到最多的可能就是“三线表”。“三线表”,一般主要由三条横线构成,当然在变量名栏里也可以拆分单元格,出现更多的线。更重要的是,“三线表”也是一种数据记录规范,以“三线表”形式记录的数...
- Python基础语法知识--变量和数据类型
-
学习Python中的变量和数据类型至关重要,因为它们构成了Python编程的基石。以下是帮助您了解Python中的变量和数据类型的分步指南:1.变量:变量在Python中用于存储数据值。它们充...
- 一文搞懂 Python 中的所有标点符号
-
反引号`无任何作用。传说Python3中它被移除是因为和单引号字符'太相似。波浪号~(按位取反符号)~被称为取反或补码运算符。它放在我们想要取反的对象前面。如果放在一个整数n...
- Python变量类型和运算符_python中变量的含义
-
别再被小名词坑哭了:Python新手常犯的那些隐蔽错误,我用同事的真实bug拆给你看我记得有一次和同事张姐一起追查一个看似随机崩溃的脚本,最后发现罪魁祸首竟然是她把变量命名成了list。说实话...
- 从零开始:深入剖析 Spring Boot3 中配置文件的加载顺序
-
在当今的互联网软件开发领域,SpringBoot无疑是最为热门和广泛应用的框架之一。它以其强大的功能、便捷的开发体验,极大地提升了开发效率,成为众多开发者构建Web应用程序的首选。而在Spr...
- Python中下划线 ‘_’ 的用法,你知道几种
-
Python中下划线()是一个有特殊含义和用途的符号,它可以用来表示以下几种情况:1在解释器中,下划线(_)表示上一个表达式的值,可以用来进行快速计算或测试。例如:>>>2+...
- 解锁Shell编程:变量_shell $变量
-
引言:开启Shell编程大门Shell作为用户与Linux内核之间的桥梁,为我们提供了强大的命令行交互方式。它不仅能执行简单的文件操作、进程管理,还能通过编写脚本实现复杂的自动化任务。无论是...
- 一文学会Python的变量命名规则!_python的变量命名有哪些要求
-
目录1.变量的命名原则3.内置函数尽量不要做变量4.删除变量和垃圾回收机制5.结语1.变量的命名原则①由英文字母、_(下划线)、或中文开头②变量名称只能由英文字母、数字、下画线或中文字所组成。③英文字...
- 更可靠的Rust-语法篇-区分语句/表达式,略览if/loop/while/for
-
src/main.rs://函数定义fnadd(a:i32,b:i32)->i32{a+b//末尾表达式}fnmain(){leta:i3...
- C++第五课:变量的命名规则_c++中变量的命名规则
-
变量的命名不是想怎么起就怎么起的,而是有一套固定的规则的。具体规则:1.名字要合法:变量名必须是由字母、数字或下划线组成。例如:a,a1,a_1。2.开头不能是数字。例如:可以a1,但不能起1a。3....
- Rust编程-核心篇-不安全编程_rust安全性
-
Unsafe的必要性Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:与C代码交互实现底层系统编程优化性能关键代码实现某些编译器无法验证的安全操作Rus...
- 探秘 Python 内存管理:背后的神奇机制
-
在编程的世界里,内存管理就如同幕后的精密操控者,确保程序的高效运行。Python作为一种广泛使用的编程语言,其内存管理机制既巧妙又复杂,为开发者们提供了便利的同时,也展现了强大的底层控制能力。一、P...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- opacity 属性 (32)
- transition 属性 (33)
- 1-1. 变量声明 (31)
