在Python面向对象编程中,类变量和实例变量是最基础却最容易混淆的概念。它们像双胞胎一样相似,却在内存管理、作用域和生命周期上有着本质区别。本文通过20个代码案例,用最直观的方式揭开它们的神秘面纱。

类变量是定义在类内部、方法外部的变量,所有实例共享同一份数据。就像班级的班规,每个学生都要遵守同样的规则。
class Dog:
species = "Canis familiaris" # 类变量
def __init__(self, name):
self.name = name # 实例变量
实例变量是每个对象独有的属性,通过self在方法内部创建。就像学生的学号,每个人都不相同。
dog1 = Dog("Buddy")
dog2 = Dog("Max")
print(dog1.species) # 输出: Canis familiaris
print(dog2.name) # 输出: Max
类变量存储在类的__dict__中,所有实例共享同一块内存;实例变量存储在各自对象的__dict__中。
class Counter:
count = 0 # 类变量
def __init__(self):
self.value = 0 # 实例变量
c1 = Counter()
c2 = Counter()
print(Counter.__dict__) # 包含'count': 0
print(c1.__dict__) # 包含'value': 0
print(c2.__dict__) # 包含'value': 0
当实例访问属性时,Python遵循"实例→类→父类"的查找链。这种机制导致了有趣的覆盖现象。
class Shape:
color = "red" # 类变量
class Circle(Shape):
pass
c = Circle()
print(c.color) # 输出: red (继承类变量)
c.color = "blue" # 创建实例变量
print(c.color) # 输出: blue (优先访问实例变量)
print(Circle.color) # 输出: red (类变量未被修改)
直接通过实例修改类变量会意外创建实例变量,而通过类名修改才是正确方式。
class Team:
members = 0
t1 = Team()
t2 = Team()
# 错误方式:创建了实例变量
t1.members = 5
print(t1.members) # 5 (实例变量)
print(t2.members) # 0 (类变量未变)
print(Team.members) # 0 (类变量未变)
# 正确方式:通过类名修改
Team.members = 10
print(t1.members) # 5 (实例变量优先)
print(t2.members) # 10 (访问类变量)
类变量适合实现全局计数器,记录所有实例的创建数量。
class User:
total_users = 0 # 类变量计数器
def __init__(self, name):
self.name = name
User.total_users += 1 # 通过类名修改
u1 = User("Alice")
u2 = User("Bob")
print(User.total_users) # 输出: 2
类变量可作为实例属性的默认值,节省内存空间。
class Configuration:
timeout = 30 # 默认超时时间
def __init__(self, custom_timeout=None):
if custom_timeout is not None:
self.timeout = custom_timeout # 覆盖默认值
cfg1 = Configuration()
cfg2 = Configuration(custom_timeout=60)
print(cfg1.timeout) # 30
print(cfg2.timeout) # 60
类变量实现多个实例间的数据共享,类似全局变量但更安全。
class Cache:
cache_data = {} # 共享缓存
@classmethod
def get(cls, key):
return cls.cache_data.get(key)
@classmethod
def set(cls, key, value):
cls.cache_data[key] = value
c1 = Cache()
c2 = Cache()
c1.set("name", "Python")
print(c2.get("name")) # 输出: Python
类方法(@classmethod)可以访问和修改类变量,静态方法(@staticmethod)则与类完全解耦。
class Pizza:
radius = 10 # 类变量
@classmethod
def double_radius(cls):
cls.radius *= 2
@staticmethod
def calculate_area(r):
return 3.14 * r * r
Pizza.double_radius()
print(Pizza.radius) # 20
print(Pizza.calculate_area(5)) # 78.5 (不依赖类变量)
__slots__优化内存使用__slots__可以限制实例变量,同时阻止动态创建实例属性。
class OptimizedClass:
__slots__ = ['x', 'y'] # 只允许这两个实例变量
class_var = "fixed"
def __init__(self, x, y):
self.x = x
self.y = y
o = OptimizedClass(1, 2)
o.z = 3 # 抛出AttributeError
通过描述符可以实现类变量级别的访问控制,如类型检查。
class TypeCheck:
def __init__(self, expected_type):
self.expected_type = expected_type
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"{self.name} must be {self.expected_type}")
instance.__dict__[self.name] = value
class Person:
age = TypeCheck(int) # 类变量描述符
def __init__(self, age):
self.age = age # 触发描述符的__set__
p = Person("twenty") # 抛出TypeError
问题:通过实例修改本应是类变量的属性,导致数据不一致。
class BankAccount:
interest_rate = 0.05 # 类变量利率
def __init__(self, balance):
self.balance = balance
def apply_interest(self):
self.interest_rate = 0.06 # 错误!创建了实例变量
self.balance *= (1 + self.interest_rate)
a1 = BankAccount(1000)
a2 = BankAccount(2000)
a1.apply_interest()
print(BankAccount.interest_rate) # 仍然是0.05
print(a1.interest_rate) # 0.06 (仅这个实例被修改)
修复:始终通过类名修改类变量。
def apply_interest(self):
BankAccount.interest_rate = 0.06 # 正确方式
self.balance *= (1 + BankAccount.interest_rate)
问题:类变量在多线程环境下可能被多个线程同时修改,导致数据竞争。
import threading
class Counter:
count = 0
def increment(self):
Counter.count += 1
def worker():
for _ in range(10000):
c = Counter()
c.increment()
threads = [threading.Thread(target=worker) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(Counter.count) # 可能小于100000
修复:使用线程锁保护类变量修改。
import threading
class ThreadSafeCounter:
count = 0
lock = threading.Lock()
def increment(self):
with ThreadSafeCounter.lock:
ThreadSafeCounter.count += 1
问题:子类意外修改了父类的类变量,影响所有父类实例。
class Vehicle:
wheels = 4
class Bicycle(Vehicle):
pass
class Car(Vehicle):
pass
Bicycle.wheels = 2 # 修改子类类变量
print(Car.wheels) # 输出2 (意外修改了父类)
修复:在子类中重新定义类变量,或使用实例变量。
class Bicycle(Vehicle):
wheels = 2 # 重新定义子类类变量
print(Car.wheels) # 输出4 (父类未受影响)
类变量在内存效率上明显优于实例变量,特别是当实例数量庞大时。
import sys
class MemoryTest:
class_var = [1, 2, 3] # 类变量列表
def __init__(self):
self.instance_var = [1, 2, 3] # 实例变量列表
# 创建10000个实例
instances = [MemoryTest() for _ in range(10000)]
# 测量内存占用(简化版)
# 实际测试可使用memory_profiler库
print("类变量方案内存更节省")
类变量的访问速度略快于实例变量,但差异通常可以忽略。
import timeit
class SpeedTest:
class_var = 0
def __init__(self):
self.instance_var = 0
def test_class_var():
t = SpeedTest()
for _ in range(1000000):
_ = SpeedTest.class_var
def test_instance_var():
t = SpeedTest()
for _ in range(1000000):
_ = t.instance_var
print("类变量访问时间:", timeit.timeit(test_class_var, number=10))
print("实例变量访问时间:", timeit.timeit(test_instance_var, number=10))
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。