【问题标题】:How different is type.__setattr__ from object.__setattr__?type.__setattr__ 与 object.__setattr__ 有何不同?
【发布时间】:2017-09-30 01:53:29
【问题描述】:

type.__setattr__ 用于类,基本上是元类的实例。另一方面,object.__setattr__ 用于类的实例。这是完全可以理解的。

我没有看到这两种方法之间有显着差异,至少在 Python 级别,我注意到这两种方法使用相同的属性分配过程,如果我错了,请纠正我:

假设a是一个用户自定义类的实例,只是一个普通类:

class A:
    pass

a = A()
a.x = ...

然后a.x = .. 调用type(a).__setattr__(...) 执行以下步骤:

注意:type(a).__setattr__ 将在 object 内置类中找到 __setattr__

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__ 将在 type 内置类中找到 __setattr__

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

【问题讨论】:

    标签: python metaclass


    【解决方案1】:

    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-&gt;tp_flags &amp; Py_TPFLAGS_HEAPTYPE)) 检查禁止对用 C 编写的类型进行属性分配,例如 intnumpy.array,因为在这些类型上分配属性会严重破坏 Python 内部结构,而不熟悉 C API 的人可能不会期待。

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

    【讨论】:

    • 关于你提到的第二步的一个问题。假设Klass 最初没有__contains__。并且__contains__ 是在创建类之后分配的。如果我分配给这样的类:Klass.__contains__ = ...PyObject_GenericSetAttr 将更新Klass.__dict__update_slot 将更新__contain__ 的插槽?这也意味着Klass__contains__ 插槽在分配之前最初是Null
    • @direprobs: update_slot 将设置与__contains__ 对应的槽。该插槽是-&gt;tp_as_sequence-&gt;sq_contains,不仅仅是sq_contains 为空,它甚至可能还不存在,因为tp_as_sequence 本身可能为空。 (除非您需要使用 C API,否则这些对您来说都不重要。)
    猜你喜欢
    • 1970-01-01
    • 2011-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多