Python中__setattr__和__set__的优先级问题
测试代码
class P(object):
def __get__(self, obj, type):
return '__get__'
def __set__(self, name, value):
print('__set__')
def __delete__(self, name):
print('__delete__')
class A(object):
p = P()
def __getattr__(self, name):
return '__getattr__'
def __setattr__(self, name, value):
print('__setattr__')
def __delattr__(self, name):
print('__delattr__')
a = A()
print(a.p)
a.p = 1
del a.p
结果分别是 __get__, __setattr__, __delattr__
经过测试,__get__优先级大于__getattr__,而__setattr__大于__set__, __delattr__大于__delete__。
猜测是 descriptor 是由 object 的__getattribute__调用的,不管 data descriptor 还是 non-data like descriptor,在实例上调用同名的属性,__get__先于__getattr__。
但不太明白__setattr__和__delattr__优先级为何更大,这样设计的目的是什么?实现上是 object 的__setattr__里面根据情况调用描述器的__set__,所以实例__setattr__的优先级更高?求告知。
Python中__setattr__和__set__的优先级问题
获取不存在的属性时才会调用 getattr
在Python中,__setattr__和__set__的优先级问题其实是个经典的属性访问机制问题。简单来说,__set__的优先级高于__setattr__。
让我用代码给你讲清楚:
class Descriptor:
"""这是一个实现了__set__的描述符"""
def __set__(self, obj, value):
print(f"描述符的__set__被调用: 设置值为 {value}")
self._value = value
class MyClass:
# 类属性,是一个描述符实例
attr = Descriptor()
def __setattr__(self, name, value):
print(f"__setattr__被调用: {name} = {value}")
super().__setattr__(name, value)
# 测试
obj = MyClass()
obj.attr = 100 # 这里会先调用描述符的__set__
运行结果:
描述符的__set__被调用: 设置值为 100
看到了吗?__set__先被调用,__setattr__根本没执行。
背后的机制是这样的:
- 当你执行
obj.attr = 100时,Python首先检查attr是不是一个描述符 - 如果是描述符且有
__set__方法,就直接调用__set__ - 只有不是描述符时,才会调用
__setattr__
再给你看个更明显的例子:
class MyClass2:
def __setattr__(self, name, value):
print(f"__setattr__被调用: {name} = {value}")
super().__setattr__(name, value)
obj2 = MyClass2()
obj2.normal_attr = 200 # 这不是描述符,所以走__setattr__
运行结果:
__setattr__被调用: normal_attr = 200
总结一下调用链:
obj.attr = value
↓
检查attr是否是数据描述符(有__set__)
↓
是 → 调用attr.__set__(obj, value)
↓
否 → 调用obj.__setattr__('attr', value)
所以记住这个顺序:描述符的__set__ > 实例的__setattr__。这是Python属性查找机制的一部分,理解这个对写高级的类设计很有帮助。
简单建议:用描述符控制属性访问,用__setattr__处理通用属性设置。
这个知道,主要想问,比如上面调用 a.p = 1 和 del a.p 时,描述器的__set__和__delete__为什么没有被调用,注释掉__setattr__和__delattr__后就会被调用。
我感觉是命名失误…
和__setattr__对标的应该是__getattribute__ 因为只有 get 情况才需要考虑是否存在
所以有了__getattr__
http://python.jobbole.com/88582/ 可以参考下这篇文章
这个是个好问题。。同意 说的命名失误
The search order that Python uses for attributes goes like this:
1. getattribute and setattr
2. Data descriptors, like property
3. Instance variables from the object’s dict (when setting an attribute, the search ends here)
4. Non-Data descriptors (like methods) and other class variables
5. getattr
来源: https://stackoverflow.com/questions/15750522/class-properties-and-setattr
感谢

