給類型對象設置類型和基類信息

內置類對象雖然在底層靜態定義好了,但是還不夠完善。解釋器在啓動之後還要再打磨一下,然後才能得到我們平時使用的類型對象,而這個過程被稱爲類型對象的初始化。

類型對象的初始化,是通過 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編程學習圈,每日干貨分享

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