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

Python 实例属性和类属性

Python 的类和对象是面向对象编程的核心,而属性(attributes)是类和对象中存储数据的地方。Python 的属性分为实例属性类属性,这俩虽然听起来差不多,但用法和行为有挺大的区别。

参考文章:Python 实例属性和类属性 | 简单一点学习 easyeasy.me

1. 什么是实例属性和类属性?

1.1 实例属性

实例属性是属于某个具体对象(实例)的属性。每个对象都有自己的实例属性,互不干扰。就像你和你的朋友每人有自己的手机,手机里的照片、设置啥的都是独一无二的。

  • 特点
    • 通常在 __init__ 方法中通过 self 定义。
    • 每个实例的属性值可以不同。
    • 只能通过实例访问(obj.attribute)。

1.2 类属性

类属性是属于整个类的属性,所有实例共享同一份数据。就像一个班级的班规,所有学生都得遵守同一套规则。

  • 特点
    • 定义在类内部,但不在任何方法内(通常在类开头定义)。
    • 通过类名或实例都可以访问(ClassName.attributeobj.attribute)。
    • 修改类属性会影响所有实例(除非实例覆盖了它)。

2. 怎么定义和使用这两种属性?

2.1 定义实例属性

实例属性一般在 __init__ 方法里用 self 来定义,每次创建对象时都会初始化。

class Dog:def __init__(self, name, age):self.name = name  # 实例属性self.age = age    # 实例属性# 创建两个 Dog 实例
dog1 = Dog("旺财", 3)
dog2 = Dog("小黑", 5)print(dog1.name)  # 输出: 旺财
print(dog2.name)  # 输出: 小黑

上面代码里,nameage 是实例属性,dog1dog2 各有一份,互不影响。

2.2 定义类属性

类属性直接在类里定义,不需要 self,通常放在 __init__ 方法外面。

class Dog:species = "Canis familiaris"  # 类属性def __init__(self, name, age):self.name = nameself.age = agedog1 = Dog("旺财", 3)
dog2 = Dog("小黑", 5)print(dog1.species)  # 输出: Canis familiaris
print(dog2.species)  # 输出: Canis familiaris
print(Dog.species)   # 输出: Canis familiaris

species 是类属性,dog1dog2Dog 类都能访问它,因为它是所有实例共享的。

3. 访问和修改属性的区别

3.1 访问属性

  • 实例属性:只能通过实例访问,比如 dog1.name
  • 类属性:可以通过类名(Dog.species)或实例(dog1.species)访问。
class Dog:species = "Canis familiaris"def __init__(self, name):self.name = namedog = Dog("旺财")
print(dog.name)      # 实例属性,输出: 旺财
print(dog.species)   # 类属性,输出: Canis familiaris
print(Dog.species)   # 类属性,输出: Canis familiaris

3.2 修改属性

  • 实例属性:修改只影响当前实例。
  • 类属性:通过类名修改会影响所有实例;通过实例修改会“遮盖”类属性(实际上是为实例创建了一个同名的实例属性)。
class Dog:species = "Canis familiaris"def __init__(self, name):self.name = namedog1 = Dog("旺财")
dog2 = Dog("小黑")# 修改类属性
Dog.species = "Canis lupus"
print(dog1.species)  # 输出: Canis lupus
print(dog2.species)  # 输出: Canis lupus# 通过实例“修改”类属性(实际上创建了实例属性)
dog1.species = "Doggo"
print(dog1.species)  # 输出: Doggo(实例属性)
print(dog2.species)  # 输出: Canis lupus(类属性)
print(Dog.species)   # 输出: Canis lupus(类属性)

注意:dog1.species = "Doggo" 并不是修改了类属性,而是给 dog1 创建了一个新的实例属性 species,遮盖了类属性。

4. 实际场景中的用法

4.1 实例属性的典型场景

实例属性适合存储每个对象独有的数据,比如名字、年龄、ID 等等。就像下面这个例子:

class Student:def __init__(self, name, student_id):self.name = nameself.student_id = student_idself.grades = []def add_grade(self, grade):self.grades.append(grade)student1 = Student("小明", "001")
student2 = Student("小红", "002")student1.add_grade(90)
student2.add_grade(85)print(student1.grades)  # 输出: [90]
print(student2.grades)  # 输出: [85]

每个学生有自己的成绩单,互不干扰,这就是实例属性的典型用法。

4.2 类属性的典型场景

类属性适合存储所有实例共享的数据,比如计数器、默认配置等。比如下面这个例子,用类属性来统计创建了多少个对象:

class Dog:count = 0  # 类属性,用来计数def __init__(self, name):self.name = nameDog.count += 1  # 每次创建实例,计数加 1dog1 = Dog("旺财")
dog2 = Dog("小黑")
dog3 = Dog("大黄")print(Dog.count)  # 输出: 3

count 是类属性,记录了总共创建了多少只狗,所有的狗共享这个计数器。

5. 注意事项和常见坑

5.1 实例属性和类属性的命名冲突

如果实例属性和类属性同名,实例属性会“遮盖”类属性。前面已经看到过例子了,这种情况要小心,别搞混了。

5.2 可变类属性的陷阱

如果类属性是可变对象(比如列表或字典),所有实例共享同一份数据,修改会影响所有实例。

class Dog:tricks = []  # 类属性,是一个列表def __init__(self, name):self.name = namedef add_trick(self, trick):self.tricks.append(trick)dog1 = Dog("旺财")
dog2 = Dog("小黑")dog1.add_trick("坐下")
dog2.add_trick("握手")print(dog1.tricks)  # 输出: ['坐下', '握手']
print(dog2.tricks)  # 输出: ['坐下', '握手']

这可能不是你想要的!因为 tricks 是类属性,所有狗共享同一个列表。如果你想要每只狗有自己的技能列表,应该用实例属性:

class Dog:def __init__(self, name):self.name = nameself.tricks = []  # 实例属性def add_trick(self, trick):self.tricks.append(trick)dog1 = Dog("旺财")
dog2 = Dog("小黑")dog1.add_trick("坐下")
dog2.add_trick("握手")print(dog1.tricks)  # 输出: ['坐下']
print(dog2.tricks)  # 输出: ['握手']

5.3 动态添加实例属性

Python 允许动态给实例添加属性,但要小心,这可能会让代码不好维护。

class Dog:def __init__(self, name):self.name = namedog = Dog("旺财")
dog.age = 3  # 动态添加实例属性
print(dog.age)  # 输出: 3

虽然很灵活,但建议尽量在 __init__ 里定义所有属性,保持代码清晰。

6. 总结

  • 实例属性:每个对象独有,定义在 __init__ 里,用 self 访问,适合存储对象特有的数据。
  • 类属性:所有实例共享,定义在类里,用类名或实例访问,适合存储共享数据。
  • 注意事项:小心命名冲突和可变类属性的陷阱,尽量在 __init__ 里定义实例属性。
http://www.xdnf.cn/news/1274527.html

相关文章:

  • 为wordpress顶部header.php文件中调用不同的标题和摘要
  • H3C(基于Comware操作系统)与eNSP平台(模拟华为VRP操作系统)的命令差异
  • Shell脚本-了解i++和++i
  • 堆(Java实现)
  • Spark学习(Pyspark)
  • 整数规划-分支定界
  • 【软件测试】BUG篇 — 详解
  • ATF(TF-A)安全通告 TFV-13(CVE-2024-7881)
  • 33.搜索旋转排序数组
  • ECharts 的理解和简单应用笔记
  • Gin vs Beego vs Echo:三大主流 Go Web 框架深度对比
  • 使用Blender可视化多传感器坐标系转换
  • sqli-labs-master/Less-51~Less-61
  • 文件 IO
  • MySQL 子查询
  • 大模型时代的机器人研究趋势:从多模态融合到高效迁移
  • Flutter 与 Android NDK 集成实战:实现高性能原生功能
  • wordpress文章摘要调用的3种方法
  • AI(1)-神经网络(正向传播与反向传播)
  • String AOP、事务、缓存
  • Java数据结构——LinkedList
  • Python与MySQL数据库交互实践:自动化数据插入系统
  • Radiology:经颅交流电刺激调节轻度阿尔茨海默病皮层与海马功能连接
  • 【Docker实战】将Django应用容器化的完整指南
  • YOLOv8算法改进--通过yaml文件添加注意力机制【附代码】
  • 从Redisson源码角度深入理解Redis分布式锁的正确实现
  • JavaScript垃圾回收机制
  • 106-基于Flask的重庆充电桩投建数据可视化分析系统
  • Redis 监控与优化方案(C++项目)
  • ShadowKV 机制深度解析:高吞吐长上下文 LLM 推理的 KV 缓存“影子”方案