如何解决在这种情况下,为什么 __setattr__ 和 __delattr__ 会引发 AttributeError ?
在 Python 中,如果类型具有 update 属性,那么 object.__setattr__
和 type.__setattr__
在属性 update 期间引发 AttributeError
的基本原理是什么?没有 __set__
方法的 em>data 描述符?同样,如果该类型具有一个 data 没有 object.__delattr__
方法的描述符?
我之所以这么问是因为我注意到 type.__delattr__
和 AttributeError
not 在属性 lookup 期间引发了 __delete__
,如果该类型有一个属性,它是一个 data 描述符,没有 object.__getattribute__
方法。
这是一个简单的程序,说明一方面通过 type.__getattribute__
查找属性(未引发 AttributeError
)与通过 __get__
更新属性和通过 {{ 删除属性之间的区别1}} 另一方面(object.__getattribute__
被提升):
AttributeError
这是另一个简单的程序,说明一方面通过 object.__setattr__
查找属性(未引发 object.__delattr__
)与通过 AttributeError
更新属性和通过 {{ 删除属性之间的区别1}} 另一方面(class DataDescriptor1: # missing __get__
def __set__(self,instance,value): pass
def __delete__(self,instance): pass
class DataDescriptor2: # missing __set__
def __get__(self,owner=None): pass
def __delete__(self,instance): pass
class DataDescriptor3: # missing __delete__
def __get__(self,owner=None): pass
def __set__(self,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
以进行属性更新和属性删除。属性查找从实例字典返回一个值,所以我想知道为什么属性更新和属性删除不使用实例字典(就像如果类型没有作为数据描述符的属性时那样)。>
解决方法
我认为这只是 C 级设计的结果,没有人真正考虑或关心。
在C级,__set__
和__delete__
对应相同的C级slot,tp_descr_set
,通过向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__,3);
}
if (res == NULL)
return -1;
Py_DECREF(res);
return 0;
}
槽没有办法说“哎呀,没有找到方法,回到正常处理”,它没有尝试。它也不会尝试模拟正常处理——这很容易出错,因为“正常处理”是类型相关的,并且它不知道要为所有类型模拟什么。如果槽包装器没有找到该方法,它只会引发异常。
如果 __set__
和 __delete__
获得了两个插槽,这种效果就不会发生,但是有人在设计 API 时必须关心,我怀疑有人这样做了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。