gpt4 book ai didi

python - 为什么 __setattr__ 和 __delattr__ 在这种情况下引发 AttributeError?

转载 作者:行者123 更新时间:2023-12-05 02:43:48 37 4
gpt4 key购买 nike

在 Python 中,object.__setattr__type.__setattr__ 在属性 update< 期间引发 AttributeError 的基本原理是什么/em> 如果类型有一个属性是一个数据 描述符,没有__set__ 方法?同样,object.__delattr__type.__delattr__ 在属性 删除 期间引发 AttributeError 的基本原理是什么,如果该类型有一个属性,它是一个没有__delete__ 方法的data 描述符?

我问这个是因为我注意到 object.__getattribute__type.__getattribute__ not 引发了 AttributeError 在属性 lookup 期间,如果类型的属性是 data 描述符,没有 __get__ 方法。

这是一个简单的程序,它说明了一方面通过 object.__getattribute__ 进行属性查找(AttributeError 未引发)与通过 更新属性之间的区别另一方面,object.__setattr__object.__delattr__ 的属性删除(AttributeError 被引发):

class DataDescriptor1:  # missing __get__
def __set__(self, instance, value): pass
def __delete__(self, instance): pass

class DataDescriptor2: # missing __set__
def __get__(self, instance, owner=None): pass
def __delete__(self, instance): pass

class DataDescriptor3: # missing __delete__
def __get__(self, instance, owner=None): pass
def __set__(self, instance, value): pass

class A:
x = DataDescriptor1()
y = DataDescriptor2()
z = DataDescriptor3()

a = A()
vars(a).update({'x': 'foo', 'y': 'bar', 'z': 'baz'})

a.x
# actual: returns 'foo'
# expected: returns 'foo'

a.y = 'qux'
# actual: raises AttributeError: __set__
# expected: vars(a)['y'] == 'qux'

del a.z
# actual: raises AttributeError: __delete__
# expected: 'z' not in vars(a)

这是另一个简单的程序,它说明了一方面通过 type.__getattribute__ 进行属性查找(AttributeError 未引发)与通过 更新属性之间的区别另一方面,type.__setattr__type.__delattr__ 的属性删除(AttributeError 被引发):

class DataDescriptor1:  # missing __get__
def __set__(self, instance, value): pass
def __delete__(self, instance): pass

class DataDescriptor2: # missing __set__
def __get__(self, instance, owner=None): pass
def __delete__(self, instance): pass

class DataDescriptor3: # missing __delete__
def __get__(self, instance, owner=None): pass
def __set__(self, instance, value): pass

class M(type):
x = DataDescriptor1()
y = DataDescriptor2()
z = DataDescriptor3()

class A(metaclass=M):
x = 'foo'
y = 'bar'
z = 'baz'

A.x
# actual: returns 'foo'
# expected: returns 'foo'

A.y = 'qux'
# actual: raises AttributeError: __set__
# expected: vars(A)['y'] == 'qux'

del A.z
# actual: raises AttributeError: __delete__
# expected: 'z' not in vars(A)

我希望实例字典发生变异,而不是为属性更新和属性删除获取 AttributeError。属性查找从实例字典返回一个值,所以我想知道为什么属性更新和属性删除也不使用实例字典(就像如果类型没有作为数据描述符的属性时它们会做的那样)。

最佳答案

我认为这只是没有人真正想到或关心的 C 级设计的结果。

在C级,__set____delete__对应同一个C级slot , tp_descr_set, 和删除是通过传递一个空值来指定的。 (这类似于用于 __setattr____delattr__ 的设计,它们也对应于 a single slot,它也通过 NULL 进行删除。)

如果您实现__set____delete__,C 级插槽将设置为wrapper function寻找 __set____delete__ 并调用它:

static int
slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
{
PyObject* stack[3];
PyObject *res;
_Py_IDENTIFIER(__delete__);
_Py_IDENTIFIER(__set__);

stack[0] = self;
stack[1] = target;
if (value == NULL) {
res = vectorcall_method(&PyId___delete__, stack, 2);
}
else {
stack[2] = value;
res = vectorcall_method(&PyId___set__, stack, 3);
}
if (res == NULL)
return -1;
Py_DECREF(res);
return 0;
}

slot没办法说“哎呀,没找到方法,回去正常处理”,也不去尝试。它也不会尝试模拟正常处理——这很容易出错,因为“正常处理”是类型相关的,并且它不知道要为所有类型模拟什么。如果插槽包装器找不到该方法,它只会引发异常。

如果 __set____delete__ 有两个插槽,这种效果就不会发生,但有人在设计 API 时不得不关心,我怀疑任何人做了。

关于python - 为什么 __setattr__ 和 __delattr__ 在这种情况下引发 AttributeError?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66754075/

37 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com