资源本身的优化(治本)
2026/8/9 17:37:44
在Python类中,有两种属性:实例属性(每个对象各自拥有)和类属性(所有对象共享)。混淆这两者是最常见的OOP陷阱之一。
⌨️ 先看直观对比:
classDog:species="犬科动物"# 类属性——所有狗共享count=0# 类属性——统计狗的数量def__init__(self,name,age):self.name=name# 实例属性——每只狗独有self.age=age# 实例属性——每只狗独有Dog.count+=1# 修改类属性dog1=Dog("旺财",3)dog2=Dog("小黑",1)print(dog1.name,dog1.age)# 旺财 3 —— 实例属性print(dog2.name,dog2.age)# 小黑 1 —— 各不相同print(dog1.species,dog2.species)# 犬科动物 犬科动物 —— 共享print(Dog.count)# 2 —— 类属性classPerson:# 类属性(默认值)species="人类"planet="地球"def__init__(self,name):self.name=name# 实例属性p=Person("张三")# 访问species——实例没有,查找类属性print(p.species)# "人类" —— 从类属性来的print(Person.species)# "人类"# 给实例添加同名属性——会覆盖类属性!p.species="火星人"# ⚠️ 这是给p创建了实例属性!print(p.species)# "火星人" —— 实例属性print(Person.species)# "人类" —— 类属性没有变化!# 删除实例属性——类属性重新可见delp.speciesprint(p.species)# "人类" —— 又回到类属性了classCounter:count=0# 类属性c1=Counter()c2=Counter()# ✅ 通过类修改——影响所有实例Counter.count=100print(c1.count,c2.count)# 100 100# ⚠️ 通过实例"修改"——实际是创建了实例属性!c1.count=999# 不是修改类属性!是在c1上创建了实例属性!print(c1.count)# 999 —— 实例属性print(c2.count)# 100 —— 类属性(没有被修改)print(Counter.count)# 100 —— 类属性不变!# 💡 这就是最经典的陷阱!# c1.count = 999 看起来像是在修改类属性# 实际上是在c1上创建了一个同名的实例属性# 类属性 Counter.count 完全没有变化# 验证:c1有实例属性,c2没有print("c1.__dict__:",c1.__dict__)# {'count': 999}print("c2.__dict__:",c2.__dict__)# {}# ⚠️ 类属性如果是可变对象——所有实例真的共享它!classClassroom:students=[]# 可变类属性——所有实例共享同一个列表!def__init__(self,name):self.name=namedefadd_student(self,student_name):self.students.append(student_name)# 修改共享的列表room1=Classroom("一班")room2=Classroom("二班")room1.add_student("张三")room2.add_student("李四")# 两个教室共享了学生列表!print(room1.students)# ['张三', '李四']print(room2.students)# ['张三', '李四'] —— 一样的!# ✅ 正确的做法:在__init__中初始化classClassroomFixed:def__init__(self,name):self.name=name self.students=[]# 每个实例有自己的列表!defadd_student(self,student_name):self.students.append(student_name)# 场景一:类常量classMathConstants:PI=3.141592653589793E=2.718281828459045GOLDEN_RATIO=1.618033988749895print(MathConstants.PI)# 不需要创建实例# 场景二:实例计数器classUser:_total_users=0# 类属性——追踪创建了多少用户def__init__(self,name):self.name=name User._total_users+=1self._id=User._total_users# 当前用户的ID@classmethoddefget_total(cls):returncls._total_users u1=User("张三")u2=User("李四")u3=User("王五")print(f"共创建{User.get_total()}个用户")# 3# 场景三:默认配置classDatabaseConfig:# 类属性作为默认配置host="localhost"port=5432database="mydb"timeout=30@classmethoddefoverride(cls,**kwargs):"""批量覆盖默认配置"""forkey,valueinkwargs.items():setattr(cls,key,value)DatabaseConfig.override(host="192.168.1.100",port=5433)print(DatabaseConfig.host)# 192.168.1.100区分类属性和实例属性是Python OOP的基本功。记住那条简单的规则:在__init__中通过self.xxx定义的是实例属性,在类体中直接定义的是类属性。
💡核心要点:
| 实例属性 | 类属性 | |
|---|---|---|
| 定义位置 | __init__中self.xxx | 类体中直接定义 |
| 存储位置 | 实例.__dict__ | 类.__dict__ |
| 作用范围 | 每个实例独有 | 所有实例共享 |
| 修改方式 | 实例.xxx = value | 类名.xxx = value |
⚠️关键陷阱:实例.类属性名 = 值会创建实例属性,遮蔽类属性——而不会修改类属性!