type .__ setattr__与object .__ setattr__有何不同? [英] How different is type.__setattr__ from 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编写的类型上分配属性,例如int
或numpy.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屋!