描述符协议代码解析
这段代码展示了 Python 中描述符协议(Descriptor Protocol)的核心机制。
完整代码结构
class Descriptor:
def __get__(self, instance, owner):
return "descriptor result"
class A:
x = Descriptor()
a = A()
print(a.x) # 输出: descriptor result
第3-4行分析
def __get__(self, instance, owner):
return "descriptor result"
核心组件:
1. __get__ 方法
- 描述符协议的核心方法
- 当访问属性时自动调用
- 返回动态计算或代理的值
2. 参数说明
self # 描述符实例本身(Descriptor() 对象)
instance # 访问属性的实例对象(a)
owner # 拥有该属性的类对象(A)
工作流程
a = A()
print(a.x)
执行流程:
┌─────────────────────────────────────┐
│ 1. 访问 a.x │
│ 2. 检测到 x 是描述符 │
│ 3. 调用 Descriptor.__get__(Descriptor实例, a, A)│
│ 4. __get__ 返回 "descriptor result"│
│ 5. 打印: descriptor result │
└─────────────────────────────────────┘
参数详细说明
def __get__(self, instance, owner):
print(f"self = {self}") # <__main__.Descriptor object at 0x…>
print(f"instance = {instance}") # <__main__.A object at 0x…>
print(f"owner = {owner}") # <class '__main__.A'>
return "descriptor result"
实例访问 vs 类访问
a = A()
print(a.x) # 通过实例访问
# 调用: Descriptor.__get__(desc, a, A)
# 输出: descriptor result
print(A.x) # 通过类访问
# 调用: Descriptor.__get__(desc, None, A)
# 输出: descriptor result
完整描述符协议
描述符需要实现以下方法之一或多个:
class CompleteDescriptor:
def __get__(self, instance, owner):
"""获取属性时调用"""
if instance is None:
return self # 通过类访问
return instance._value
def __set__(self, instance, value):
"""设置属性时调用"""
instance._value = value
def __delete__(self, instance):
"""删除属性时调用"""
del instance._value
实际应用示例
1. 类型验证描述符
class Typed:
def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"Expected {self.expected_type}")
instance.__dict__[self.name] = value
class Person:
name = Typed('name', str)
age = Typed('age', int)
p = Person()
p.name = "Alice" # ✅
p.age = "30" # ❌ TypeError
2. 懒加载属性
class LazyAttribute:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
value = self.func(instance)
instance.__dict__[self.func.__name__] = value
return value
class Database:
@LazyAttribute
def connection(self):
print("Creating connection…")
return "connection object"
db = Database()
print(db.connection) # Creating connection… \\n connection object
print(db.connection) # connection object (不再打印)
描述符分类
# 数据描述符(有 __set__ 或 __delete__)
class DataDescriptor:
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
# 非数据描述符(只有 __get__)
class NonDataDescriptor:
def __get__(self, instance, owner):
pass
优先级规则
属性访问优先级(从高到低):
1. __dict__ 中定义的属性
2. 数据描述符
3. 实例属性
4. 非数据描述符
5. __getattr__()
6. 抛出 AttributeError
标准库中的描述符
# property 本质是描述符
class PropertyExample:
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
# 等价于描述符实现
class XDescriptor:
def __get__(self, instance, owner):
return instance._x
def __set__(self, instance, value):
instance._x = value
关键设计决策
1. 封装属性逻辑
- 将属性访问逻辑集中管理
- 实现类型检查、验证、懒加载等
2. 类级别的配置
- 描述符在类级别定义
- 所有实例共享同一个描述符对象
3. 元编程基础
- property、classmethod、staticmethod 都基于描述符
- ORM、表单验证框架的核心技术
学习要点
这是 Python 强大的元编程能力的核心体现,使得框架开发者可以优雅地控制属性访问行为。



