python对象的__dict__属性详解
在 Python 里,__dict__ 是大部分对象都有的一个特殊属性,它是一个字典,用来存储对象的实例属性。每个键代表属性名,对应的值就是属性的值。
一.__dict__的作用
动态存储对象属性:Python 使用 __dict__
动态管理对象的属性。支持动态增删改查:通过修改 __dict__
可以动态添加、删除或修改对象属性。
Python 对象(类实例、类本身、模块等)的属性默认存储在 __dict__
字典中。
当你执行
obj.attr = value
时,实际等同于obj.__dict__['attr'] = value
当你读取
obj.attr
时,实际等同于obj.__dict__.get('attr')
1. 实例对象的 __dict__
存储实例特有的属性(通过
self.xxx
定义的属性)不包括类属性、方法或特殊方法(如
__init__
)每个实例有独立的
__dict__
class Person:species = "Human" # 类属性(不在实例的 __dict__ 中)def __init__(self, name):self.name = name # 实例属性p = Person("Alice")
print(p.__dict__) # 输出: {'name': 'Alice'}# 动态添加属性
p.age = 30
print(p.__dict__) # 输出: {'name': 'Alice', 'age': 30}# 直接修改 __dict__
p.__dict__["job"] = "Engineer"
print(p.job) # 输出: Engineer
2. 类的 __dict__
存储类属性、方法、特殊方法等。
还包括元类信息、文档字符串等。
不包含实例属性。
class Person:species = "Human"def __init__(self, name):self.name = namedef greet(self):return f"Hello, {self.name}!"print(Person.__dict__)
# 输出包含:
# 'species': 'Human',
# '__init__': <function ...>,
# 'greet': <function ...>,
# '__module__': '__main__', ...
3. 内置类型
内置类型(如 list
, str
)没有 __dict__:
print([].__dict__) # AttributeError: 'list' object has no attribute '__dict__'
- 并非所有对象都有
__dict__
属性,像一些内置类型(如int
、list
)和使用__slots__
的类就没有。- 直接操作
__dict__
可能会破坏类的封装性,所以要谨慎使用。
二.注意事项
1.操作`__dict__`比点号访问稍快,但破坏封装性
性能测试代码:
import timeitclass TestClass:def __init__(self):self.value = 0# 创建测试对象
obj = TestClass()# 测试点号访问
dot_access = timeit.timeit("obj.value += 1", globals=globals(), number=1000000
)# 测试__dict__访问
dict_access = timeit.timeit("obj.__dict__['value'] += 1", globals=globals(), number=1000000
)print(f"点号访问耗时: {dot_access:.6f} 秒")
print(f"__dict__访问耗时: {dict_access:.6f} 秒")
print(f"性能提升: {(dot_access - dict_access)/dot_access:.2%}")
典型输出结果:
点号访问耗时: 0.120387 秒
__dict__访问耗时: 0.097652 秒
性能提升: 18.88%
原因分析:
点号访问流程:
__dict__直接访问:
直接操作字典,跳过属性查找机制
避免描述符协议(
@property
等)的调用不触发
__getattr__
/__setattr__
等特殊方法
封装性破坏示例:
class BankAccount:def __init__(self, balance):self._balance = balance # 私有属性@propertydef balance(self):"""通过属性访问器控制访问"""return self._balance@balance.setterdef balance(self, value):if value < 0:raise ValueError("余额不能为负")self._balance = value# 正常使用
acc = BankAccount(100)
acc.balance = 50 # 通过setter方法# 绕过封装性
acc.__dict__["_balance"] = -100 # 直接修改私有属性
print(acc.balance) # 输出: -100 (违反业务规则)
2. 安全风险:对象一致性破坏
示例1:绕过验证逻辑
class TemperatureSensor:def __init__(self):self._temperature = 0@propertydef temperature(self):return self._temperature@temperature.setterdef temperature(self, value):if not (-100 <= value <= 200):raise ValueError("无效温度值")self._temperature = valuesensor = TemperatureSensor()# 合法操作
sensor.temperature = 25# 非法操作 - 直接修改__dict__
sensor.__dict__["_temperature"] = 300 # 绕过验证
print(sensor.temperature) # 输出300 (无效值!)
示例2:破坏内部状态一致性
class ShoppingCart:def __init__(self):self.items = {}self._update_total()def add_item(self, item, price):self.items[item] = priceself._update_total()def _update_total(self):"""内部方法维护一致性"""self.total = sum(self.items.values())def __repr__(self):return f"<Cart items={self.items} total={self.total}>"cart = ShoppingCart()
cart.add_item("Apple", 1.5)
cart.add_item("Banana", 0.8)
print(cart) # <Cart items={'Apple':1.5, 'Banana':0.8} total=2.3># 直接修改__dict__破坏一致性
cart.__dict__["items"]["Orange"] = 2.0 # 添加新商品
print(cart) # <Cart items={...} total=2.3> # 总计未更新!# 更危险的修改
del cart.__dict__["total"] # 删除关键属性
print(cart) # 访问cart.total将引发AttributeError
示例3:干扰内置属性
class Logger:def log(self, message):print(f"LOG: {message}")logger = Logger()# 危险操作 - 覆盖类方法
logger.__dict__["log"] = "I'm not a function anymore!"# 尝试调用方法
logger.log("Test") # TypeError: 'str' object is not callable
最佳实践总结
场景 | 推荐做法 | 风险做法 |
---|---|---|
属性访问 | 使用点号操作 obj.attr | 直接操作 obj.__dict__['attr'] |
动态属性 | 使用 setattr(obj, 'attr', value) | 直接修改 __dict__ |
序列化 | 实现 __getstate__ 方法 | 直接使用 __dict__ 序列化 |
调试 | 使用 vars(obj) 函数 | 直接访问 obj.__dict__ |
三.安全使用原则
仅将 __dict__
用于只读操作(如调试、日志)
# 安全做法:查看属性
print(vars(obj)) # 等价于 obj.__dict__ 的只读视图
需要修改时优先使用封装方法:
# 替代直接操作__dict__
setattr(obj, 'new_attr', value)
class SecureData:__slots__ = ('x', 'y') # 禁止动态属性def __init__(self, x, y):self.x = xself.y = yobj = SecureData(1, 2)
obj.z = 3 # AttributeError
需要动态行为时重写特殊方法:
class DynamicAttributes:def __getattr__(self, name):"""安全处理缺失属性"""return f"Default value for {name}"def __setattr__(self, name, value):"""统一控制属性设置"""# 添加验证逻辑...super().__setattr__(name, value)
关键类使用 __slots__
防止意外修改:
结论:虽然 __dict__ 提供了强大的底层访问能力,但应像对待 __init__ 中的 self 一样谨慎——它是实现机制而非接口设计。99% 的场景中,点号操作符和属性描述符是更安全、更可维护的选择。