# 实例属性和类属性有什么区别?
面试官问:"Python实例属性和类属性有什么区别?"
很多人会说"一个属于对象,一个属于类",但没说清楚存储位置和修改影响。真正要讲清楚的是:实例属性存在对象的__dict__里,每个对象独立;类属性存在类的__dict__里,所有对象共享。 访问时实例属性优先,但修改时不会影响类属性。
# 简要回答
实例属性是对象自己的属性,每个对象独立拥有。 通常在__init__方法里通过self.attr = value创建,存储在对象的__dict__里。修改一个对象的实例属性不会影响其他对象。
类属性是类本身的属性,所有实例共享。 定义在类体里、方法外面,存储在类的__dict__里。修改类属性会影响所有没有同名实例属性的对象。
访问属性时,Python先查对象的__dict__(实例属性),再查类的__dict__(类属性)。 所以同名时实例属性优先。但通过self.attr = value赋值时,只会在对象的__dict__里创建或修改实例属性,不会修改类属性。
# 详细回答
# 定义和存储位置
class Dog:
species = "Canis familiaris" # 类属性,定义在类体
def __init__(self, name, age):
self.name = name # 实例属性,通过self创建
self.age = age # 实例属性
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)
print(dog1.__dict__) # {'name': 'Buddy', 'age': 3}
print(dog2.__dict__) # {'name': 'Max', 'age': 5}
print(Dog.__dict__['species']) # Canis familiaris
2
3
4
5
6
7
8
9
10
11
12
13
name和age是实例属性,每个对象的__dict__里都有一份;species是类属性,只在Dog.__dict__里有一份。
# 访问优先级
读取属性时,实例属性优先:
class Counter:
count = 0 # 类属性
def __init__(self):
self.count = 10 # 实例属性,同名
c = Counter()
print(c.count) # 10,找到实例属性
print(Counter.count) # 0,类属性没变
2
3
4
5
6
7
8
9
访问c.count时,Python先查c.__dict__,找到了实例属性count=10,就不再往上查类属性。
# 修改影响范围
修改实例属性只影响这个对象:
class Dog:
species = "Canis"
dog1 = Dog()
dog2 = Dog()
dog1.species = "Felis" # 给dog1创建了实例属性
print(dog1.species) # Felis
print(dog2.species) # Canis,dog2仍然读到类属性
print(Dog.species) # Canis,类属性没变
2
3
4
5
6
7
8
9
10
dog1.species = "Felis"在dog1.__dict__里创建了实例属性,所以dog1.species现在读到的是实例属性。dog2和Dog.species都没受影响。
修改类属性会影响所有没有同名实例属性的对象:
class Dog:
species = "Canis"
dog1 = Dog()
dog2 = Dog()
Dog.species = "Canis familiaris" # 修改类属性
print(dog1.species) # Canis familiaris
print(dog2.species) # Canis familiaris
dog1.species = "Mixed" # 给dog1创建实例属性
Dog.species = "Updated" # 再次修改类属性
print(dog1.species) # Mixed,不受影响
print(dog2.species) # Updated,仍然读类属性
2
3
4
5
6
7
8
9
10
11
12
13
14

# 可变对象做类属性的坑
类属性如果是列表或字典等可变对象,所有实例会共享它,容易踩坑:
class Team:
members = [] # 类属性,可变对象
def __init__(self, name):
self.name = name
def add_member(self, member):
self.members.append(member)
team1 = Team("Alpha")
team2 = Team("Beta")
team1.add_member("Alice")
team2.add_member("Bob")
print(team1.members) # ['Alice', 'Bob'],不符合预期!
print(team2.members) # ['Alice', 'Bob'],两个队伍共享一个列表
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
members是类属性,team1和team2的add_member都在修改同一个列表。正确做法是把可变对象放到实例属性里:
class Team:
def __init__(self, name):
self.name = name
self.members = [] # 实例属性,每个对象独立
team1 = Team("Alpha")
team2 = Team("Beta")
team1.members.append("Alice")
team2.members.append("Bob")
print(team1.members) # ['Alice']
print(team2.members) # ['Bob']
2
3
4
5
6
7
8
9
10
11
12
13
# 什么时候用类属性
类属性适合存储所有对象共享的配置或常量:
class Circle:
PI = 3.14159 # 所有圆共享的常量
count = 0 # 统计创建了多少个圆
def __init__(self, radius):
self.radius = radius
Circle.count += 1 # 每创建一个对象,计数器加1
def area(self):
return Circle.PI * self.radius ** 2
c1 = Circle(5)
c2 = Circle(10)
print(Circle.count) # 2
2
3
4
5
6
7
8
9
10
11
12
13
14
类属性也可以当作全局配置、版本号、默认值。如果需要跨所有对象统计或共享,用类属性;如果每个对象独立,用实例属性。
# 知识拓展
Q1:通过self能修改类属性吗?
不能直接修改,但能遮盖。self.attr = value只会在对象的__dict__里创建或修改实例属性。如果要修改类属性,必须通过类名:
class Test:
value = 10
t = Test()
t.value = 20 # 创建实例属性
print(t.value) # 20
print(Test.value) # 10,类属性没变
Test.value = 30 # 修改类属性
print(Test.value) # 30
2
3
4
5
6
7
8
9
10
Q2:删除实例属性后会怎样?
会继续读到类属性:
class Test:
value = "class"
t = Test()
t.value = "instance"
print(t.value) # instance
del t.value
print(t.value) # class,现在读到类属性
2
3
4
5
6
7
8
9
Q3:类属性能是方法吗?
方法本质上也是类属性,只是值是一个函数对象。访问obj.method时,Python返回一个绑定方法对象,把obj绑定到self上。
Q4:能用__slots__限制实例属性吗?
可以。定义__slots__后,对象不再有__dict__,只能有__slots__里列出的属性。这能节省内存,但失去了动态添加属性的灵活性:
class Limited:
__slots__ = ['x', 'y']
obj = Limited()
obj.x = 10 # 可以
obj.z = 20 # AttributeError: 'Limited' object has no attribute 'z'
2
3
4
5
6
Q5:如果类属性是不可变对象,修改它会怎样?
不可变对象(数字、字符串、元组)的"修改"实际是创建新对象并重新绑定名字。通过self.attr += 1会在对象的__dict__里创建实例属性:
class Test:
count = 0
t = Test()
t.count += 1 # 等价于 t.count = t.count + 1
print(t.count) # 1
print(Test.count) # 0,类属性没变
2
3
4
5
6
7
评论
验证登录状态...