Python描述符__get__方法代码解析:含义、原理及替代方案
一、__get__方法的基本作用
这段代码是描述符协议中的__get__方法:当你通过实例访问绑定了描述符的属性时(比如obj.my_attr),Python会自动调用该方法;如果通过类访问(比如MyClass.my_attr),instance参数会是None,owner参数始终是属性所属的类。
二、instance.__dict__.get(self.name)的作用拆解
instance.__dict__:每个Python实例都自带一个__dict__字典,专门存储该实例独有的属性(和类属性区分开)。比如你给obj.x = 10赋值,x就会被存在obj.__dict__里。self.name:这是描述符实例自身的属性,通常是在描述符绑定到类属性时自动设置的(比如Python 3.6+的__set_name__方法),对应它在类中的属性名。例如:class MyClass: my_attr = MyDescriptor() # 此时MyDescriptor实例的self.name会被设为'my_attr'.get(self.name):从实例的__dict__字典中取出self.name对应的属性值,如果这个属性不存在,就返回None(不会像直接用[]索引那样抛出KeyError)。为什么不用
getattr(instance, self.name)?因为getattr会再次触发描述符的__get__方法,导致无限递归——毕竟你就是在__get__里调用它,等于自己调用自己死循环。
三、替代实现方式
1. 用底层属性获取方法避免递归
如果不想直接操作__dict__,可以用object.__getattribute__——这是Python最底层的属性获取方法,不会触发描述符协议:
def __get__(self, instance, owner=None): print(f'__get__(instance={instance}, owner={owner})') try: return object.__getattribute__(instance, self.name) except AttributeError: return None
这段代码和原逻辑效果一致:属性不存在时返回None;如果不需要处理不存在的情况,直接去掉try-except即可,此时属性不存在会抛出AttributeError。
2. 将值存储在描述符内部(适合代理/隔离场景)
如果不想把属性值存在实例的__dict__里,可以用WeakKeyDictionary存储每个实例对应的值,既不会污染实例的__dict__,还能避免内存泄漏:
from weakref import WeakKeyDictionary class MyDescriptor: def __init__(self): self._instance_values = WeakKeyDictionary() def __set_name__(self, owner, name): self.name = name def __get__(self, instance, owner=None): print(f'__get__(instance={instance}, owner={owner})') if instance is None: return self # 类访问时返回描述符自身 return self._instance_values.get(instance) def __set__(self, instance, value): self._instance_values[instance] = value
这种方式下,每个实例的属性值都存在描述符的_instance_values字典里,通过实例作为键关联对应的值。
内容的提问来源于stack exchange,提问作者madhur

