Python 3.12 Special Attribute - 02 - __dict__

张开发
2026/5/30 3:48:32 15 分钟阅读
Python 3.12 Special Attribute - 02 - __dict__
Python 3.12 Special Attribute -__dict____dict__是 Python 中最重要的内置特殊属性之一。它存储了对象的可写属性动态属性是 Python 对象动态性的核心体现。通过__dict__我们可以查看、修改或删除对象的属性甚至可以动态地添加新属性。理解__dict__对于掌握 Python 对象模型、元编程以及调试至关重要。本文将详细解析__dict__的定义、在不同对象上的表现、底层实现、使用场景及注意事项。1.__dict__的基本概念__dict__是一个字典或字典的只读代理用于存储对象的属性。具体类型取决于对象类型对于实例instance__dict__是一个普通的 Python 字典存储该实例特有的属性。对于类class__dict__是一个mappingproxy对象只读视图存储类属性包括方法、类变量等。对于模块module__dict__是一个字典存储模块的全局变量。__dict__的存在使得 Python 对象可以在运行时动态地添加、修改和删除属性这是 Python 动态类型语言特性的重要体现。2. 不同对象上的__dict__2.1 实例的__dict__每个实例默认拥有一个__dict__字典用于存储该实例独有的属性。classPerson:def__init__(self,name):self.namename pPerson(Alice)p.age30print(p.__dict__)# {name: Alice, age: 30}实例的__dict__可以直接修改p.__dict__[city]Beijingprint(p.city)# Beijing2.2 类的__dict__类的__dict__是一个mappingproxy对象它提供了类属性的只读视图。你不能直接通过cls.__dict__[attr] value来修改类属性但可以通过cls.attr value间接修改。classPerson:speciesHomo sapiensdefgreet(self):returnHelloprint(Person.__dict__)# mappingproxy({__module__: __main__, species: Homo sapiens, greet: function Person.greet at ..., ...})# 通过类属性赋值来修改Person.speciesHomo sapiens sapiensprint(Person.__dict__[species])# 新值# 直接修改 __dict__ 会引发 TypeError# Person.__dict__[species] Homo # TypeError: mappingproxy object does not support item assignment2.3 模块的__dict__每个模块都有一个__dict__字典存储模块的全局变量。实际上globals()函数返回的就是当前模块的__dict__。# 在模块 my_module.py 中x10print(globals())# {x: 10, __name__: __main__, ...}3. 底层实现机制在 CPython 中每个 PyObject 都有一个指向其类型对象的指针ob_type以及一个指向__dict__的指针如果存在。对于实例__dict__是一个普通的PyDictObject。对于类__dict__是一个PyDictProxyObject即mappingproxy它封装了类的内部字典并提供只读访问。当访问obj.attr时Python 会首先在实例的__dict__中查找除非是数据描述符。如果找不到再到类的__dict__中查找以此类推。这种查找机制实现了属性的继承和覆盖。__dict__的底层实现使得 Python 对象可以动态地增加属性但同时也带来了内存开销每个实例都有一个字典。为了优化内存可以使用__slots__来禁止实例拥有__dict__从而节省内存。4. 使用场景与示例4.1 动态添加/修改属性通过操作__dict__可以在运行时动态地为对象添加属性。classDynamic:passobjDynamic()obj.__dict__[new_attr]100print(obj.new_attr)# 100逐行解析创建空类Dynamic。创建实例obj。直接操作obj.__dict__字典添加键new_attr和值100。访问obj.new_attr时Python 会从__dict__中读取该值。为什么这样写这展示了 Python 的动态性允许在运行时添加任意属性而不需要在类定义中预先声明。4.2 序列化与反序列化利用__dict__可以方便地将对象转换为字典用于 JSON 序列化。classUser:def__init__(self,name,age):self.namename self.ageage userUser(Alice,30)datauser.__dict__# {name: Alice, age: 30}importjson json_strjson.dumps(data)print(json_str)# {name: Alice, age: 30}逐行解析定义User类。创建实例user。获取user.__dict__字典。使用json.dumps将字典转换为 JSON 字符串。为什么这样写这是一种快速将对象属性导出为可序列化格式的方法常用于日志、存储或网络传输。4.3 批量设置属性可以使用update方法批量更新实例属性。classDynamic:passobjDynamic()# 先创建实例 objattrs{name:Bob,age:25}obj.__dict__.update(attrs)# 批量设置属性print(obj.name)# Bobprint(obj.age)# 25为什么这样写避免了多次赋值代码更简洁。4.4 调试与自省在调试时打印__dict__可以快速查看对象的所有属性。classPerson:def__init__(self,name,age):self.namename self.ageage pPerson(Alice,30)print(p.__dict__)# {name: Alice, age: 30}为什么这样写这是一种快速了解对象状态的方式比逐个打印属性更高效。4.5 实现属性访问代理通过重写__getattr__和__setattr__可以结合__dict__实现代理模式。classProxy:def__init__(self,target):self._targettargetdef__getattr__(self,name):returngetattr(self._target,name)def__setattr__(self,name,value):ifname_target:super().__setattr__(name,value)else:setattr(self._target,name,value)def__repr__(self):returnfProxy({self._target})# 使用targetPerson(Alice,30)proxyProxy(target)print(proxy.name)# Aliceproxy.age31print(target.age)# 31为什么这样写通过__dict__存储目标对象代理类可以转发属性访问。4.6 与__slots__的关系当类定义了__slots__时实例不再拥有__dict__从而节省内存。通过__slots__属性可实现不可变对象。classPoint:__slots__(x,y)def__init__(self,x,y):self.xx self.yy pPoint(1,2)# print(p.__dict__) # AttributeError: Point object has no attribute __dict__为什么这样写对于需要创建大量实例的场景使用__slots__可以显著减少内存占用。5. 注意事项与陷阱不要依赖__dict__的存在使用__slots__的类没有__dict__因此代码应该使用hasattr或getattr而不是直接访问__dict__。类的__dict__是只读的不能直接修改cls.__dict__但可以通过cls.attr value修改。直接操作__dict__会绕过描述符和__setattr__例如使用obj.__dict__[attr] value不会触发__setattr__可能导致不一致。性能考虑频繁访问__dict__比直接属性访问稍慢因为需要字典查找。但在大多数场景下差异可忽略。6. 底层实现细节CPython在 CPython 中每个对象的类型对象PyTypeObject有一个tp_dictoffset字段表示实例字典在对象内存中的偏移量。对于默认类这个偏移量指向一个PyDictObject。当访问obj.__dict__时Python 会通过偏移量找到字典对象。类的__dict__则存储在类型对象自身的tp_dict字段中并通过PyDictProxy_New创建一个只读代理返回。7. 总结特性说明角色存储对象的动态属性是 Python 动态性的基础类型实例dict类mappingproxy模块dict可变性实例和模块的__dict__可写类的__dict__只读用途动态属性管理、序列化、调试、代理模式底层CPython 中通过tp_dictoffset和tp_dict实现注意事项避免绕过__setattr__注意与__slots__的兼容性掌握__dict__是深入理解 Python 对象模型的关键。它让我们能够以字典的形式操作对象属性为元编程和动态行为提供了无限可能。但同时也需要谨慎使用以免破坏封装或导致意外行为。如果在学习过程中遇到问题欢迎在评论区留言讨论!

更多文章