type.__setattr__
用于类,基本上是元类的实例。另一方面,object.__setattr__
用于类的实例。这完全可以理解。
我没有看到这两种方法之间存在显着差异,至少在Python级别,我注意到两者使用相同的属性赋值程序,如果我错了,请纠正我:
假设a
是用户定义类的实例,只是普通类:
class A:
pass
a = A()
a.x = ...
然后a.x = ..
调用type(a).__setattr__(...)
执行以下步骤:
注意:type(a).__setattr__
会在__setattr__
内置课程中找到object
1)在type(a).__mro__
中查找数据描述符。
2)如果找到了数据描述符,请调用其__set__
方法并退出。
3)如果在type(a).__mro__
中找不到数据描述符,则将属性添加到a.__dict__
,a.__dict__['x'] = ...
使用类 - 元类实例,过程类似:
class A(metaclass=type):
pass
然后:A.x = ...
被翻译为type(A).__setattr__(...)
,执行以下步骤:
注意:type(A).__setattr__
会在__setattr__
内置课程中找到type
1)在type(A).__mro__
2)如果找到了数据描述符,请调用其__set__
方法并退出。
3)如果在type(A).__mro__
中找不到数据描述符,则将属性添加到A.__dict__
,a.__dict__['x'] = ...
但object.__setattr__
不适合上课:
>>> 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__
仅限于类object.__setattr__
仅限于类的实例?
答案 0 :(得分:7)
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
调用更新类型的dict或从元类调用适当的描述符之后,update_slot
修复受属性影响的任何插槽分配。这些插槽是C级函数指针,它们实现了像实例分配,in
检查,+
,解除分配等功能。大多数都有相应的Python级方法,如__contains__
或{ {1}},如果重新分配了其中一个Python级方法,则必须更新相应的插槽(或槽)。 __add__
还会更新类的所有后代上的插槽,并使用于类型对象属性的内部属性高速缓存中的条目无效。