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__的优先级问题

8 回复

获取不存在的属性时才会调用 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__根本没执行。

背后的机制是这样的:

  1. 当你执行 obj.attr = 100 时,Python首先检查attr是不是一个描述符
  2. 如果是描述符且有__set__方法,就直接调用__set__
  3. 只有不是描述符时,才会调用__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

回到顶部