哈喽,大家好,我就是那个不喜欢在大厂搬砖,不喜欢在研究院做研究,只喜欢创业做计算机底层课程的coder,子牙老师
写篇文章与大家分享一下Python虚拟机CPython实现面向对象机制的细节
Python是编译型语言,coder编写的Python代码,编译生成的是Python虚拟机CPython才能够识别的字节码,而不是操作系统上能够直接运行的机器码
比如coder写一个Python类 class Stu: pass
生成的字节码长这样 那你有没有想过:CPython执行完这些字节码指令,类Stu在虚拟机内部长啥样?
如果你写过这样的Python代码 class Stu: passprint(type(Stu))你会发现它的输出结果是
这个type就是类Stu在虚拟机CPython中的样子,对应的C语言结构体是PyHeapTypeObject 从Python中的class Stu,到C语言中的PyHeapTypeObject,中间的过程是怎样的呢?言外之意就是,任何一门编程语言,它的类是如何转变成C语言中的struct的呢
以下,enjoy
字节码指令LOAD_BUILD_CLASS当CPython的执行引擎执行到指令LOAD_BUILD_CLASS时,会调用到函数builtin___build_class__
builtin___build_class__(……){ …… meta = (PyObject *) (&PyType_Type); ……}
PyType_Type是什么呢?是所有Python 类型(类对象)的类型对象(metaclass),你可以理解成是所有类对象的基类 PyType_Type有几个重要的函数,在创建Python类对应的类对象时,都会用上 比如type_call,每个Python类对应的类对象,就是在这个函数中创建的
随着代码往后面走,会进入函数_PyObject_MakeTpCall
_PyObject_MakeTpCall(……) { …… ternaryfunc call = Py_TYPE(callable)->tp_call; result = call(callable, argstuple, kwdict); ……}
代码进入PyType_Type的type_call
创建PyHeapTypeObjecttype_call的核心逻辑 type_call(……) { …… obj = type->tp_new(type, args, kwds); …… int res = type->tp_init(obj, args, kwds);}type_new的核心逻辑 type_new(……) { …… type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots); et = (PyHeapTypeObject *)type;}这里面这个tp_alloc,PyType_Type中初始赋值是0,后面会被赋值为函数PyType_GenericAlloc。论证一下 执行完type_call,Python类对应的类对象就创建完成
这个流程研究完我就想:一、创建出来的类对象PyHeapTypeObject存储在哪?二、创建出来的类对象PyHeapTypeObject与GC之间的关系是如何建立的
存储在哪?在CPython眼中,一个.py文件是一个模块,对应的结构体是 每个模块对象(PyModuleObject)都关联一个 dict,这个 dict 是变量作用域字典,用于存储模块的所有全局变量,也就是 Python 中的 globals()。除了 globals,执行过程中还有 locals(局部变量表)和 builtins(内建作用域)。
当我们定义一个类时,例如 class Stu:,Python 实际创建了一个 PyHeapTypeObject* 作为类对象,并将它以 {"Stu": } 的形式插入模块的 dict 中,从而在全局作用域中完成类名与类型对象的绑定。 PyHeapTypeObject也是一个Object,就会参与GC,那PyHeapTypeObject与GC是如果关联起来的呢?
与GC的关系CPython运行起来,会创建一个运行时环境 其中generations就是分代收集器,创建的Object挂到generations的链表中,就会被扫描到。链表就是PyGC_Head 内存回收时,如果ob_refcnt=0,就会被回收。那PyHeapTypeObject与GC的关系是如何建立的呢?答案就在函数PyType_GenericAlloc PyType_GenericAlloc(……) { …… if (_PyType_IS_GC(type)) { obj = _PyObject_GC_Malloc(size); }else { obj = (PyObject *)PyObject_MALLOC(size); } if (_PyType_IS_GC(type)) { _PyObject_GC_TRACK(obj); }}_PyObject_GC_Malloc与PyObject_MALLOC的区别是,_PyObject_GC_Malloc申请内存时会多申请一个PyGC_Head,挂到垃圾收集器中需要 真正挂到垃圾收集器中是_PyObject_GC_TRACK #define _PyObject_GC_TRACK(op) _PyObject_GC_TRACK_impl(__FILE__, __LINE__, _PyObject_CAST(op))_PyObject_GC_TRACK_impl(……) { …… PyGC_Head *generation0 = tstate->interp->gc.generation0; PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev); _PyGCHead_SET_NEXT(last, gc); _PyGCHead_SET_PREV(gc, last); _PyGCHead_SET_NEXT(gc, generation0); generation0->_gc_prev = (uintptr_t)gc; ……}
至此,Python虚拟机CPython为一个用户自定义的Python类创建PyHeapTypeObject全部完成
创建对象基于用户自定义的Python类创建对象,比如 class Stu: passstu = Stu()
代码流程: Stu PyHeapTypeObject.tp_call Stu PyHeapTypeObject.tp_new Stu PyHeapTypeObject.tp_alloc PyType_GenericAlloc一个Python对象就是这样创建出来的~
|