type .__ setattr__与object .__ setattr__有何不同? [英] How different is type.__setattr__ from object.__setattr__?

查看:286
本文介绍了type .__ setattr__与object .__ setattr__有何不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

type.__setattr__用于类,基本上是元类的实例.另一方面,object.__setattr__用于类的实例.这是完全可以理解的.

type.__setattr__ is used for classes, basically instances of metaclasses. object.__setattr__ on the other hand, is used for instances of classes. This is totally understood.

至少在Python级别上,我没有看到这两种方法之间的显着差异,我注意到这两种方法使用相同的属性分配过程,如果我错了,请纠正我:

I don't see a significant difference between the two method, at least at Python level, I notice the two use the same procedures for attribute assignment, correct me if I'm wrong:

假设a是用户定义的类的实例,只是一个普通的类:

Suppose a is an instance of a user-defined class, just a normal class:

class A:
    pass

a = A()
a.x = ...

然后a.x = ..调用type(a).__setattr__(...)执行以下步骤:

then a.x = .. invokes type(a).__setattr__(...) which performs the following steps:

注意:type(a).__setattr__将在object内置类

Note: type(a).__setattr__ will find __setattr__ in object builtin class

1)在type(a).__mro__中查找数据描述符.

1) Look for a data descriptor in type(a).__mro__.

2)如果找到了数据描述符,则调用其__set__方法并退出.

2) If a data descriptor was found, call its __set__ method and exit.

3)如果在type(a).__mro__中未找到数据描述符,则将属性添加到a.__dict__a.__dict__['x'] = ...

3) If no data descriptor was found in type(a).__mro__, then add attribute to a.__dict__, a.__dict__['x'] = ...

对于类-元类的实例,过程类似:

With classes--instances of metaclasses, the process is similar:

class A(metaclass=type):
    pass

然后:A.x = ...被翻译为type(A).__setattr__(...),它执行以下步骤:

then: A.x = ... is translated to type(A).__setattr__(...) which performs the following steps:

注意:type(A).__setattr__将在type内置类

Note: type(A).__setattr__ will find __setattr__ in type builtin class

1)在type(A).__mro__

2)如果找到了数据描述符,则调用其__set__方法并退出.

2) If a data descriptor was found, call its __set__ method and exit.

3)如果在type(A).__mro__中未找到数据描述符,则将属性添加到A.__dict__a.__dict__['x'] = ...

3) If no data descriptor was found in type(A).__mro__, then add attribute to A.__dict__, a.__dict__['x'] = ...

但是object.__setattr__不适用于类:

>>> object.__setattr__(A, 'x', ...)
TypeError: can't apply this __setattr__ to type object

反之亦然,type.__setattr__对于A的实例无效:

and vice versa, type.__setattr__ doesn't work for instances of A:

>>> type.__setattr__(A(), 'x', ...)
TypeError: descriptor '__setattr__' requires a 'type' object but received a 'A'

嗯!两种方法之间必须有所不同.这是微妙的,但是仍然是正确的!

Hmmm! There must be something different between the two methods. This is subtle, but true nonetheless!

大概这两个方法在__setattr__内部执行相同的步骤,type.__setattr__object.__setattr__有什么区别,以便type.__setattr__限于类,而object.__setattr__限于类的实例?

Presumably the two methods perform the same steps inside __setattr__, what is the difference between type.__setattr__ and object.__setattr__ so that type.__setattr__ is limited to classes and object.__setattr__ is limited to instances of classes?

推荐答案

type.__setattr__进行了检查以防止在诸如int之类的类型上设置属性,并且它执行了一堆常规对象不需要的不可见清理.

type.__setattr__ has a check to prevent setting attributes on types like int, and it does a bunch of invisible cleanup that isn't needed for normal objects.

让我们一起来看看吧!这是 type.__setattr__ :

Let's take a look under the hood! Here's type.__setattr__:

static int
type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
{
    if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
        PyErr_Format(
            PyExc_TypeError,
            "can't set attributes of built-in/extension type '%s'",
            type->tp_name);
        return -1;
    }
    if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
        return -1;
    return update_slot(type, name);
}

,如果我们检查 PyBaseObject_Type ,我们看到它使用PyObject_GenericSetAttr作为其__setattr__,该调用出现在type_setattro中途.

and if we examine PyBaseObject_Type, we see it uses PyObject_GenericSetAttr for its __setattr__, the same call that appears halfway through type_setattro.

因此,type.__setattr__类似于object.__setattr__,但周围还有一些其他处理.

Thus, type.__setattr__ is like object.__setattr__, but with some additional handling wrapped around it.

首先,if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))检查禁止在用C编写的类型上分配属性,例如intnumpy.array,因为在这些类型上分配属性可能会严重破坏Python内部结构,从而使某些不熟悉C API的人可能无法期待.

First, the if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) check prohibits attribute assignment on types written in C, like int or numpy.array, because assigning attributes on those can seriously screw up the Python internals in ways someone unfamiliar with the C API might not expect.

第二,在PyObject_GenericSetAttr调用更新类型的字典或从元类调用适当的描述符之后,update_slot修复受属性分配影响的任何插槽.这些插槽是C级功能指针,实现了实例分配,in检查,+,释放等功能,其中大多数具有对应的Python级方法,例如__contains____add__,如果有的话这些Python级别方法中的一个已重新分配,相应的插槽也必须更新. update_slot还会更新该类所有后代的插槽,并使用于类型对象属性的内部属性缓存中的条目无效.

Second, after the PyObject_GenericSetAttr call updates the type's dict or calls an appropriate descriptor from the metaclass, update_slot fixes up any slots affected by the attribute assignment. These slots are C-level function pointers that implement functionality like instance allocation, in checks, +, deallocation, etc. Most of them have corresponding Python-level methods, like __contains__ or __add__, and if one of those Python-level methods is reassigned, the corresponding slot (or slots) have to be updated, too. update_slot also updates slots on all descendants of the class, and it invalidates entries in an internal attribute cache used for type object attributes.

这篇关于type .__ setattr__与object .__ setattr__有何不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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