从对象类型.__ setattr__有多么不同.__ setattr __?

时间:2017-05-02 13:48:49

标签: python metaclass

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__仅限于类的实例?

1 个答案:

答案 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语言编写的类型进行属性赋值,例如intnumpy.array,因为在这些类型上分配属性会严重搞砸Python内部人员的方式使用C API可能不会期望。

其次,在PyObject_GenericSetAttr调用更新类型的dict或从元类调用适当的描述符之后,update_slot修复受属性影响的任何插槽分配。这些插槽是C级函数指针,它们实现了像实例分配,in检查,+,解除分配等功能。大多数都有相应的Python级方法,如__contains__或{ {1}},如果重新分配了其中一个Python级方法,则必须更新相应的插槽(或槽)。 __add__还会更新类的所有后代上的插槽,并使用于类型对象属性的内部属性高速缓存中的条目无效。