给类型对象设置类型和基类信息

内置类对象虽然在底层静态定义好了,但是还不够完善。解释器在启动之后还要再打磨一下,然后才能得到我们平时使用的类型对象,而这个过程被称为类型对象的初始化。

类型对象的初始化,是通过 PyType_Ready 函数实现的,我们来看一下,它位于 Objects/typeobject.c 中。另外由于初始化这部分内容比较多,接下来我们准备用三篇文章去介绍它。

int
PyType_Ready(PyTypeObject *type)
{   
    //这里的参数显然是类型对象
    //以 <class 'type'> 为例
    
    //dict:属性字典
    //bases:继承的所有基类,即 __bases__
    PyObject *dict, *bases;
    
    //base:继承的第一个基类,即 __base__
    PyTypeObject *base;
    Py_ssize_t i, n;
    
    //......
    //......
    
    //获取类型对象中 tp_base 成员指定的基类
    base = type->tp_base;
    if (base == NULL && type != &PyBaseObject_Type) {
        //如果基类为空、并且该类本身不是object
        //那么将该类的基类设置为 object、即 &PyBaseObject_Type
        //所以一些类型对象在底层定义的时候,tp_base 成员为空
        //因为tp_base是在这里、也就是初始化的时候进行设置的
        base = type->tp_base = &PyBaseObject_Type;
        Py_INCREF(base);
    }

    //如果基类不是NULL,也就是指定了基类
    //但是基类的属性字典是NULL
    if (base != NULL && base->tp_dict == NULL) {
        //说明该类的基类尚未初始化,那么会先对基类进行初始化
        //注意这里的 tp_dict,它表示每个类都会有的属性字典
        //而属性字典是否为 NULL,是类型对象是否初始化完成的重要标志
        if (PyType_Ready(base) < 0)
            goto error;
    }

    //如果该类型对象的 ob_type 为空,但是基类不为空
    //那么将该类型对象的 ob_type 设置为基类的 ob_type
    //为什么要做这一步, 我们后面会详细说
    if (Py_TYPE(type) == NULL && base != NULL)
        Py_TYPE(type) = Py_TYPE(base);

    //获取 __bases__,检测是否为空
    bases = type->tp_bases;
    //如果为空,则根据 __base__ 进行设置
    if (bases == NULL) {
        //如果 base 也为空,那么 bases 就是空元祖
        //而base如果为空了,说明当前的类对象一定是object
        if (base == NULL)
            bases = PyTuple_New(0);
        //如果 base 不为空,那么 bases 就是 (base,)
        else
            bases = PyTuple_Pack(1, base);
        if (bases == NULL)
            goto error;
        //设置 tp_bases
        type->tp_bases = bases;
    }

    //设置属性字典,后续再聊
    dict = type->tp_dict;
    if (dict == NULL) {
        dict = PyDict_New();
        if (dict == NULL)
            goto error;
        type->tp_dict = dict;
    }
    //......
}

对于指定了tb_base的类对象,当然就使用指定的基类,而对于没有指定tp_base的类对象,虚拟机将为其指定一个默认的基类:&PyBaseObject_Type ,也就是 Python 的 object。

现在我们看到 PyType_Type 的 tp_base 指向了 PyBaseObject_Type,这在Python中体现的就是 type 继承自 object、或者说 object 是 type 的父类。但是所有的类的 ob_type 又都指向了 PyType_Type,包括 object,因此我们又说 type 是包括 object 在内的所有类的类(元类)。

而在获得了基类之后,会判断基类是否被初始化,如果没有,则需要先对基类进行初始化。可以看到,判断初始化是否完成的条件是tp_dict是否为NULL,这符合之前的描述。对于内置类对象来说,在解释器启动的时候,就已经作为全局对象存在了,所以它们的初始化不需要做太多工作,只需小小的完善一下即可,比如设置基类、类型、以及对 tp_dict 进行填充。

在基类设置完毕后,会继续设置 ob_type,这个ob_type就是 class 返回的类型对象。

首先 PyType_Ready 函数里面接收的是一个 PyTypeObject 对象,我们知道这个在 Python 中就是类对象。因此这里是设置这些类对象的 ob_type,那么对应的显然就是元类(metaclass),我们自然会想象到Python的type。

而Py_TYPE(type) = Py_TYPE(base)这一行代码是把父类的ob_type设置成了当前类的ob_type,那么这一步的意义何在呢?我们使用Python来演示一下。


class MyType(type):
    pass

class A(metaclass=MyType):
    pass

class B(A):
    pass
    
print(type(A))  # <class '__main__.MyType'>
print(type(B))  # <class '__main__.MyType'>

我们看到B继承了A,而A的类型是MyType,那么B的类型也成了MyType。也就是说 A 是由 XX 生成的,那么B在继承A之后,B 也会由 XX 生成,所以源码中的那一步就是用来做这件事情的。另外,这里之所以用 XX 代替,是因为 Python 里面不仅仅只有 type 是元类,那些继承了 type 的子类也可以是元类。

而且如果你熟悉 flask 的话,你会发现 flask 源码里面就有类似于这样的操作:

class MyType(type):

    def __new__(mcs, name, bases, attrs):
        # 关于第一个参数我们需要说一下
        # 对于一般的类来说这里应该是cls
        # 但我们这里是元类,所以应该用mcs,意思就是metaclass
        # 我们额外设置一些属性吧,关于元类我们后续会介绍
        # 虽然目前还没有看底层实现,但至少使用方法应该知道
        attrs.update({"name": "古明地觉"})
        return super().__new__(mcs, name, bases, attrs)

def with_metaclass(meta, bases=(object, )):
    return meta("", bases, {})

class Girl(with_metaclass(MyType, (int,))):
    pass
    
print(type(Girl))  # <class '__main__.MyType'>
print(getattr(Girl, "name"))  # 古明地觉
print(Girl("123"))  # 123

所以逻辑很清晰了,虚拟机就是将基类的metaclass设置为子类的metaclass。对于当前的PyType_Type来说,其metaclass就是object的metaclass,也是它自己。而在源码的PyBaseObject_Type中也可以看到,其ob_type被设置成了 &PyType_Type。

if (Py_TYPE(type) == NULL && base != NULL)
        Py_TYPE(type) = Py_TYPE(base);

tb_base 和 ob_type 设置完毕之后,会设置 tb_bases。tb_base 对应 __base__,tb_bases 对应 __bases__,我们用 Python 演示一下,这两者的区别。

class A:
    pass

class B(A):
    pass

class C:
    pass

class D(B, C):
    pass
    
print(D.__base__)  # <class '__main__.B'>
print(D.__bases__)  # (<class '__main__.B'>, <class '__main__.C'>)

print(C.__base__)  # <class 'object'>
print(C.__bases__)  # (<class 'object'>,)

print(B.__base__)  # <class '__main__.A'>
print(B.__bases__)  # (<class '__main__.A'>,)

我们看到 D 同时继承多个类,那么 tp_base 就是先出现的那个基类。而 tp_bases 则是继承的所有基类,但是基类的基类是不会出现的,比如 object。对于 B 而言也是一样的。

然后我们看看 C,因为 C 没有显式地继承任何类,那么 tp_bases 就是NULL。但是Python3里面所有的类都默认继承了object,所以tp_base就是object。而 tp_bases,显然是 (object,)。

以上就是 tp_base、ob_type、tp_bases 的设置,还是比较简单的,它们在设置完毕之后,就要对 tp_dict 进行填充了。而填充 tp_dict 是一个极其繁复的过程,我们下一篇文章再说。

以上就是本次分享的所有内容,想要了解更多欢迎前往公众号:Python编程学习圈,每日干货分享

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章