Python 类型与对象的区别有多大?

Python 类型与对象的区别有多大?,python,metaclass,Python,Metaclass,type.\uuuu setattr\uuuuu用于类,基本上是元类的实例对象。另一方面,用于类的实例。这是完全理解的 我看不出这两种方法之间有什么显著的区别,至少在Python级别,我注意到这两种方法使用相同的属性赋值过程,如果我错了,请纠正我: 假设a是一个用户定义类的实例,只是一个普通类: class A: pass a = A() a.x = ... 然后,a.x=…调用type(a)。\uuuuu setattr\uuuuuuu(…)执行以下步骤: 注意:类型(a)。\uu

type.\uuuu setattr\uuuuu
用于类,基本上是元类的实例<代码>对象。另一方面,用于类的实例。这是完全理解的

我看不出这两种方法之间有什么显著的区别,至少在Python级别,我注意到这两种方法使用相同的属性赋值过程,如果我错了,请纠正我:

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

class A:
    pass

a = A()
a.x = ...
然后,
a.x=…
调用
type(a)。\uuuuu setattr\uuuuuuu(…)
执行以下步骤:

注意:
类型(a)。\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

1) 在
类型(a)中查找数据描述符

2) 如果找到一个数据描述符,调用它的
\uuuu set\uu
方法并退出

3) 如果在
类型(a)中找不到数据描述符,则将属性添加到
a.。
a.。\uuuuuu dict\uuuu['x']=…


对于类——元类的实例,过程类似:

class A(metaclass=type):
    pass
然后:
A.x=…
被转换为
类型(A)。\uuuu setattr\uuuu(…)
,执行以下步骤:

注:
类型(A)。\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

1) 在
类型(a)中查找数据描述符

2) 如果找到一个数据描述符,调用它的
\uuuu set\uu
方法并退出

3) 如果在
类型(A)中找不到数据描述符,则将属性添加到
A.。
A.。\uuuuuu dict\uuuu['x']=…

但是
object.\uuuu setattr\uuuu
不适用于类:

>>> object.__setattr__(A, 'x', ...)
TypeError: can't apply this __setattr__ to type object
反之亦然,
type.\uuuuu setattr\uuuuu
不适用于
A
的实例:

>>> type.__setattr__(A(), 'x', ...)
TypeError: descriptor '__setattr__' requires a 'type' object but received a 'A'
嗯!!这两种方法之间肯定有些不同。这是微妙的,但确实如此


假设这两种方法在
\uuuuuu setattr\uuuuuu
中执行相同的步骤,
type.\uuuuu setattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,它还进行了一系列不可见的清理,这对于普通对象来说是不必要的


让我们看看引擎盖下面!这是:

static int
类型_setattro(PyTypeObject*类型、PyObject*名称、PyObject*值)
{
if(!(类型->tp_标志和Py_TPFLAGS_HEAPTYPE)){
Pyrr_格式(
PyExc_类型错误,
“无法设置内置/扩展类型“%s”的属性,
类型->tp_名称);
返回-1;
}
if(PyObject_genericsettr((PyObject*)类型、名称、值)<0)
返回-1;
返回更新槽(类型、名称);
}
如果我们检查,我们会看到它使用
PyObject\u GenericSetAttr
作为其
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu setattr
的调用,在
type\u setattro
的中间出现了相同的调用

因此,
type.\uuuuSetAttr\uuuuuuuu
类似于
对象。\uuuuuSetAttr\uuuuuuuuu
,但有一些附加的处理

首先,
if(!(type->tp_flags&Py_TPFLAGS_HEAPTYPE))
检查禁止对用C编写的类型进行属性分配,如
int
numpy.array
,因为在这些类型上分配属性可能会以不熟悉C-API的人可能无法预料的方式严重破坏Python内部结构


其次,在
PyObject\u GenericSetAttr
调用更新类型的dict或从元类调用适当的描述符之后,
update\u slot
修复受属性分配影响的所有插槽。这些插槽是C级函数指针,实现诸如实例分配、
检查中的
+
、解除分配等功能。它们中的大多数都有相应的Python级方法,如
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu包含
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu添加update\u slot
也会更新类的所有子体上的slot,并且它会使用于类型对象属性的内部属性缓存中的条目无效。

关于您提到的第二步的一个问题。假设
Klass
最初没有
\u contains\uu
__contains
是在类创建后分配的。如果我分配给像:
Klass这样的类。
PyObject\u GenericSettR
将更新
Klass。
dict
update\u slot
将更新
\uU contains\uCODE>的插槽。这也意味着
\uU CONTAINSTAINS\uC>的插槽将更新
ode>在赋值之前最初是
Null
?@direprobs:
update\u slot
将设置与
\uuuu contains\uuuu
相对应的插槽。该插槽是
->tp\u as\u sequence->sq sq\u contains
,并且不仅仅是
sq\u contains
为Null,它甚至可能还不存在,因为
tp\u as\u sequence
本身可能存在已经为空。(除非您需要使用C API,否则这些对您来说都不重要。)
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);
}