为什么在这种情况下__setattr__和__delattr__引发AttributeError? [英] Why do __setattr__ and __delattr__ raise an AttributeError in this case?

查看:78
本文介绍了为什么在这种情况下__setattr__和__delattr__引发AttributeError?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Python中, object .__ setattr __ type .__ setattr __ 在属性 update <期间引发 AttributeError 的理由是什么?/em>如果类型具有作为 data 描述符的属性,但没有 __ set __ 方法?同样,如果在属性删除中, object .__ delattr __ type .__ delattr __ 引发 AttributeError 的理由是什么?类型具有作为 data 描述符的属性,而没有 __ delete __ 方法?

In Python, what is the rationale for which object.__setattr__ and type.__setattr__ raise an AttributeError during attribute update if the type has an attribute which is a data descriptor without a __set__ method? Likewise, what is the rationale for which object.__delattr__ and type.__delattr__ raise an AttributeError during attribute deletion if the type has an attribute which is a data descriptor without a __delete__ method?

我之所以这样问,是因为我注意到 object .__ getattribute __ type .__ getattribute __ 确实会引发 AttributeError 在属性 lookup 期间插入code>,如果该类型具有的属性是 data 描述符,而没有 __ get __ 方法.

I am asking this because I have noticed that object.__getattribute__ and type.__getattribute__ do not raise an AttributeError during attribute lookup if the type has an attribute which is a data descriptor without a __get__ method.

这是一个简单的程序,说明了一方面通过 object .__ getattribute __ 查找属性(未引发 AttributeError )与通过更新属性之间的差异.另一方面,object .__ setattr __ 和属性被 object .__ delattr __ 删除(引发 AttributeError ):

Here is a simple program illustrating the differences between attribute lookup by object.__getattribute__ on the one hand (AttributeError is not raised), and attribute update by object.__setattr__ and attribute deletion by object.__delattr__ on the other hand (AttributeError is raised):

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 ):

Here is another simple program illustrating the differences between attribute lookup by type.__getattribute__ on the one hand (AttributeError is not raised), and attribute update by type.__setattr__ and attribute deletion by type.__delattr__ on the other hand (AttributeError is raised):

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 来进行属性更新和属性删除.属性查询从实例字典中返回一个值,所以我想知道为什么属性更新和属性删除也不使用实例字典(就像如果类型不具有作为数据描述符的属性那样,它们也会这样做).

I would expect the instance dictionary to be mutated instead of getting an AttributeError for attribute update and attribute deletion. Attribute lookup returns a value from the instance dictionary, so I am wondering why attribute update and attribute deletion do not use the instance dictionary as well (like they would do if the type did not have an attribute which is a data descriptor).

推荐答案

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

I think it's just a consequence of the C-level design that no one really thought or cared much about.

在C级别, __ set __ __ delete __ 对应于同一C级别

At C level, __set__ and __delete__ correspond to the same C-level slot, tp_descr_set, and deletion is specified by passing a null value to set. (This is similar to the design used for __setattr__ and __delattr__, which also correspond to a single slot that also gets passed NULL for deletion.)

如果实现 __ set __ __ delete __ ,则C级插槽将设置为

If you implement either __set__ or __delete__, the C-level slot gets set to a wrapper function that looks for __set__ or __delete__ and calls it:

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;
}

插槽无法说哎呀,没找到方法,回到正常处理状态",并且它不会尝试.它还不尝试模仿正常处理,因为正常处理"会导致错误,因此容易出错.是类型相关的,并且它不知道所有类型都可以模拟什么.如果插槽包装程序找不到该方法,则只会引发异常.

The slot has no way to say "oops, didn't find the method, go back to normal handling", and it doesn't try. It also doesn't try to emulate the normal handling - that would be error-prone, since "normal handling" is type-dependent, and it can't know what to emulate for all types. If the slot wrapper doesn't find the method, it just raises an exception.

如果 __ set __ __ delete __ 有两个插槽,则不会发生这种效果,但是在设计API时有人会在意,我怀疑做到了.

This effect wouldn't happen if __set__ and __delete__ had gotten two slots, but someone would have had to care while they were designing the API, and I doubt anyone did.

这篇关于为什么在这种情况下__setattr__和__delattr__引发AttributeError?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆