type.__setattr__ 与 object.__setattr__ 有何不同?
How different is type.__setattr__ from object.__setattr__?
type.__setattr__
用于 classes,基本上是 metaclasses 的实例。另一方面,object.__setattr__
用于 classes 的实例。这是完全明白的。
我看不出这两种方法有什么显着差异,至少在 Python 水平上,我注意到这两种方法使用相同的属性分配过程,如果我错了请纠正我:
假设a
是一个用户自定义class的实例,只是一个普通的class:
class A:
pass
a = A()
a.x = ...
然后 a.x = ..
调用 type(a).__setattr__(...)
执行以下步骤:
注意:type(a).__setattr__
会在 object
内置 class
中找到 __setattr__
1) 在type(a).__mro__
中查找数据描述符。
2) 如果找到数据描述符,调用它的__set__
方法并退出。
3) 如果在type(a).__mro__
中没有找到数据描述符,则将属性添加到a.__dict__
,a.__dict__['x'] = ...
With classes--metaclasses 的实例,过程类似:
class A(metaclass=type):
pass
然后:A.x = ...
被转换为 type(A).__setattr__(...)
,执行以下步骤:
注意:type(A).__setattr__
会在 type
内置 class
中找到 __setattr__
1) 在type(A).__mro__
中查找数据描述符
2) 如果找到数据描述符,调用它的__set__
方法并退出。
3) 如果在type(A).__mro__
中没有找到数据描述符,则将属性添加到A.__dict__
,a.__dict__['x'] = ...
但是 object.__setattr__
不适用于 classes:
>>> object.__setattr__(A, 'x', ...)
TypeError: can't apply this __setattr__ to type object
反之亦然,type.__setattr__
不适用于 A
的实例:
>>> type.__setattr__(A(), 'x', ...)
TypeError: descriptor '__setattr__' requires a 'type' object but received a 'A'
嗯!这两种方法之间一定有什么不同。这是微妙的,但仍然是真实的!
推测这两个方法在__setattr__
里面执行相同的步骤,type.__setattr__
和object.__setattr__
有什么区别,所以type.__setattr__
被限制为classes 和 object.__setattr__
仅限于 classes?
的实例
type.__setattr__
有一个检查以防止在 int
等类型上设置属性,并且它做了一堆普通对象不需要的不可见清理。
让我们深入了解一下!这里是 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
.
中途出现的相同调用
因此,type.__setattr__
类似于 object.__setattr__
,但围绕它进行了一些额外的处理。
首先,if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
检查禁止对用 C 编写的类型进行属性赋值,例如 int
或 numpy.array
,因为对这些类型赋值会严重搞砸 Python 不熟悉 C API 的人可能不会想到的内部方式。
其次,在 PyObject_GenericSetAttr
调用更新类型的字典或从元 class 调用适当的描述符后,update_slot
修复任何 slots 受属性分配的影响。这些槽是 C 级函数指针,实现实例分配、in
检查、+
、释放等功能。它们中的大多数都有相应的 Python 级方法,例如 __contains__
或 __add__
,如果其中一个 Python 级别的方法被重新分配,相应的插槽(或多个插槽)也必须更新。 update_slot
还更新 class 的所有后代上的槽,并使用于类型对象属性的内部属性缓存中的条目无效。
type.__setattr__
用于 classes,基本上是 metaclasses 的实例。另一方面,object.__setattr__
用于 classes 的实例。这是完全明白的。
我看不出这两种方法有什么显着差异,至少在 Python 水平上,我注意到这两种方法使用相同的属性分配过程,如果我错了请纠正我:
假设a
是一个用户自定义class的实例,只是一个普通的class:
class A:
pass
a = A()
a.x = ...
然后 a.x = ..
调用 type(a).__setattr__(...)
执行以下步骤:
注意:type(a).__setattr__
会在 object
内置 class
__setattr__
1) 在type(a).__mro__
中查找数据描述符。
2) 如果找到数据描述符,调用它的__set__
方法并退出。
3) 如果在type(a).__mro__
中没有找到数据描述符,则将属性添加到a.__dict__
,a.__dict__['x'] = ...
With classes--metaclasses 的实例,过程类似:
class A(metaclass=type):
pass
然后:A.x = ...
被转换为 type(A).__setattr__(...)
,执行以下步骤:
注意:type(A).__setattr__
会在 type
内置 class
__setattr__
1) 在type(A).__mro__
2) 如果找到数据描述符,调用它的__set__
方法并退出。
3) 如果在type(A).__mro__
中没有找到数据描述符,则将属性添加到A.__dict__
,a.__dict__['x'] = ...
但是 object.__setattr__
不适用于 classes:
>>> object.__setattr__(A, 'x', ...)
TypeError: can't apply this __setattr__ to type object
反之亦然,type.__setattr__
不适用于 A
的实例:
>>> type.__setattr__(A(), 'x', ...)
TypeError: descriptor '__setattr__' requires a 'type' object but received a 'A'
嗯!这两种方法之间一定有什么不同。这是微妙的,但仍然是真实的!
推测这两个方法在__setattr__
里面执行相同的步骤,type.__setattr__
和object.__setattr__
有什么区别,所以type.__setattr__
被限制为classes 和 object.__setattr__
仅限于 classes?
type.__setattr__
有一个检查以防止在 int
等类型上设置属性,并且它做了一堆普通对象不需要的不可见清理。
让我们深入了解一下!这里是 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
.
因此,type.__setattr__
类似于 object.__setattr__
,但围绕它进行了一些额外的处理。
首先,if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
检查禁止对用 C 编写的类型进行属性赋值,例如 int
或 numpy.array
,因为对这些类型赋值会严重搞砸 Python 不熟悉 C API 的人可能不会想到的内部方式。
其次,在 PyObject_GenericSetAttr
调用更新类型的字典或从元 class 调用适当的描述符后,update_slot
修复任何 slots 受属性分配的影响。这些槽是 C 级函数指针,实现实例分配、in
检查、+
、释放等功能。它们中的大多数都有相应的 Python 级方法,例如 __contains__
或 __add__
,如果其中一个 Python 级别的方法被重新分配,相应的插槽(或多个插槽)也必须更新。 update_slot
还更新 class 的所有后代上的槽,并使用于类型对象属性的内部属性缓存中的条目无效。