[英]How different is type.__setattr__ from object.__setattr__?
type.__setattr__
is used for classes, basically instances of metaclasses. type.__setattr__
用于类,基本上是元类的实例。 object.__setattr__
on the other hand, is used for instances of classes. 另一方面,
object.__setattr__
用于类的实例。 This is totally understood. 这完全可以理解。
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: 我没有看到这两种方法之间存在显着差异,至少在Python级别,我注意到两者使用相同的属性赋值程序,如果我错了,请纠正我:
Suppose a
is an instance of a user-defined class, just a normal class: 假设
a
是用户定义类的实例,只是一个普通类:
class A:
pass
a = A()
a.x = ...
then ax = ..
invokes type(a).__setattr__(...)
which performs the following steps: 然后
ax = ..
调用type(a).__setattr__(...)
执行以下步骤:
Note: type(a).__setattr__
will find __setattr__
in object
builtin class 注意:
type(a).__setattr__
将在object
内置类中找到__setattr__
1) Look for a data descriptor in type(a).__mro__
. 1)在
type(a).__mro__
查找数据描述符。
2) If a data descriptor was found, call its __set__
method and exit. 2)如果找到了数据描述符,则调用其
__set__
方法并退出。
3) If no data descriptor was found in type(a).__mro__
, then add attribute to a.__dict__
, a.__dict__['x'] = ...
3)如果是在没有发现数据描述符
type(a).__mro__
,然后添加属性到a.__dict__
, a.__dict__['x'] = ...
With classes--instances of metaclasses, the process is similar: 使用类 - 元类的实例,过程类似:
class A(metaclass=type):
pass
then: Ax = ...
is translated to type(A).__setattr__(...)
which performs the following steps: 然后:
Ax = ...
被转换为type(A).__setattr__(...)
,它执行以下步骤:
Note: type(A).__setattr__
will find __setattr__
in type
builtin class 注意:
type(A).__setattr__
将在type
内置类中找到__setattr__
1) Look for a data descriptor in type(A).__mro__
1)在
type(A).__mro__
查找数据描述符type(A).__mro__
2) If a data descriptor was found, call its __set__
method and exit. 2)如果找到了数据描述符,则调用其
__set__
方法并退出。
3) If no data descriptor was found in type(A).__mro__
, then add attribute to A.__dict__
, a.__dict__['x'] = ...
3)如果在
type(A).__mro__
没有找到数据描述符,则将属性添加到A.__dict__
a.__dict__['x'] = ...
, a.__dict__['x'] = ...
But object.__setattr__
doesn't work for classes: 但是
object.__setattr__
不适用于类:
>>> object.__setattr__(A, 'x', ...)
TypeError: can't apply this __setattr__ to type object
and vice versa, type.__setattr__
doesn't work for instances of A
: 反之亦然,
type.__setattr__
不适用于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!
这很微妙,但仍然是真的!
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? 据推测,这两个方法在
__setattr__
执行相同的步骤, type.__setattr__
和object.__setattr__
之间的区别是什么,因此type.__setattr__
仅限于类和object.__setattr__
仅限于类的实例?
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__
有一个检查来阻止像int
这样的类型设置属性,它会执行一堆普通对象不需要的隐形清理。
Let's take a look under the hood! 我们来看看引擎盖! Here's
type.__setattr__
: 这是
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);
}
and if we examine PyBaseObject_Type
, we see it uses PyObject_GenericSetAttr
for its __setattr__
, the same call that appears halfway through type_setattro
. 如果我们检查
PyBaseObject_Type
,我们会看到它使用PyObject_GenericSetAttr
作为__setattr__
,这是在type_setattro
中途出现的同一个调用。
Thus, type.__setattr__
is like object.__setattr__
, but with some additional handling wrapped around it. 因此,
type.__setattr__
就像object.__setattr__
,但有一些额外的处理包裹它。
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. 首先,
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
检查禁止对用C语言编写的类型进行属性赋值,比如int
或numpy.array
,因为在那些上分配属性会严重搞砸Python内部,就像有些人不熟悉的那样。 C API可能不会期望。
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. 其次,在
PyObject_GenericSetAttr
调用更新类型的dict或从元类调用适当的描述符之后, update_slot
修复受属性赋值影响的任何插槽 。 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. 这些槽是C级函数指针实现的功能像实例分配,
in
检查, +
他们中的大多数对应Python的水平的方法,如,解除分配等等__contains__
或__add__
,并且如果那些Python的电平的方法之一被重新分配,相应的插槽(或插槽)也必须更新。 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. update_slot
还会更新类的所有后代上的插槽,并使用于类型对象属性的内部属性高速缓存中的条目无效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.