Python源码学习:Python类机制分析
发布日期:2021-07-25 13:04:28 浏览次数:9 分类:技术文章

本文共 19067 字,大约阅读时间需要 63 分钟。

Python源码分析

本文环境python2.5系列参考书籍<
>

本文主要分析Python中类时如何实现的,在Python中,一切都是对象;任何对象都有一个type,都可以通过class属性,一般情况下为type对应于Python源码中的PyType_Type;在Python的类中,都直接或者间接与Object有关联,都是Object的子类,对应Python中PyBaseObject_Type。在Python的启动执行流程一文中有介绍,在Python启动的过程中会首先对默认的类型进行初始化,我们就从这里开始分析。

分析

初始化代码如下;

void_Py_ReadyTypes(void){    if (PyType_Ready(&PyType_Type) < 0)        Py_FatalError("Can't initialize 'type'");    if (PyType_Ready(&_PyWeakref_RefType) < 0)        Py_FatalError("Can't initialize 'weakref'");    if (PyType_Ready(&PyBool_Type) < 0)        Py_FatalError("Can't initialize 'bool'");    if (PyType_Ready(&PyString_Type) < 0)        Py_FatalError("Can't initialize 'str'");    if (PyType_Ready(&PyList_Type) < 0)        Py_FatalError("Can't initialize 'list'");    if (PyType_Ready(&PyNone_Type) < 0)        Py_FatalError("Can't initialize type(None)");    if (PyType_Ready(&PyNotImplemented_Type) < 0)        Py_FatalError("Can't initialize type(NotImplemented)");}

这里分析下PyType_Ready(&PyType_Type)初始化过程,首先我们查看下PyType_Type类型的结构;

PyTypeObject PyType_Type = {    PyObject_HEAD_INIT(&PyType_Type)      //  类型还是PyType_Type    0,                  /* ob_size */    "type",                 /* tp_name */    sizeof(PyHeapTypeObject),       /* tp_basicsize */    sizeof(PyMemberDef),            /* tp_itemsize */    (destructor)type_dealloc,       /* tp_dealloc */    0,                  /* tp_print */    0,                  /* tp_getattr */    0,                  /* tp_setattr */    type_compare,               /* tp_compare */    (reprfunc)type_repr,            /* tp_repr */    0,                  /* tp_as_number */    0,                  /* tp_as_sequence */    0,                  /* tp_as_mapping */    (hashfunc)_Py_HashPointer,      /* tp_hash */    // 哈希函数    (ternaryfunc)type_call,         /* tp_call */    // tp_call函数    0,                  /* tp_str */    (getattrofunc)type_getattro,        /* tp_getattro */    (setattrofunc)type_setattro,        /* tp_setattro */    0,                  /* tp_as_buffer */    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |        Py_TPFLAGS_BASETYPE,        /* tp_flags */    type_doc,               /* tp_doc */    (traverseproc)type_traverse,        /* tp_traverse */    (inquiry)type_clear,            /* tp_clear */    0,                  /* tp_richcompare */    offsetof(PyTypeObject, tp_weaklist),    /* tp_weaklistoffset */    0,                  /* tp_iter */    0,                  /* tp_iternext */    type_methods,               /* tp_methods */    type_members,               /* tp_members */    type_getsets,               /* tp_getset */    0,                  /* tp_base */    0,                  /* tp_dict */    0,                  /* tp_descr_get */    0,                  /* tp_descr_set */    offsetof(PyTypeObject, tp_dict),    /* tp_dictoffset */    0,                  /* tp_init */    0,                  /* tp_alloc */    type_new,               /* tp_new */    PyObject_GC_Del,                /* tp_free */    (inquiry)type_is_gc,            /* tp_is_gc */};

由此可见type定义中的tp_base为空,在初始化过程中就会将基类换成Object;我们看完定义后再查看初始化过程;

intPyType_Ready(PyTypeObject *type){    PyObject *dict, *bases;    PyTypeObject *base;    Py_ssize_t i, n;    if (type->tp_flags & Py_TPFLAGS_READY) {
assert(type->tp_dict != NULL); return 0; } assert((type->tp_flags & Py_TPFLAGS_READYING) == 0); type->tp_flags |= Py_TPFLAGS_READYING;#ifdef Py_TRACE_REFS /* PyType_Ready is the closest thing we have to a choke point * for type objects, so is the best place I can think of to try * to get type objects into the doubly-linked list of all objects. * Still, not all type objects go thru PyType_Ready. */ _Py_AddToAllObjects((PyObject *)type, 0);#endif /* Initialize tp_base (defaults to BaseObject unless that's us) */ base = type->tp_base; // 获取初始化类型的基类 if (base == NULL && type != &PyBaseObject_Type) { // 如果基类为空,并且初始化的类型不为Object base = type->tp_base = &PyBaseObject_Type; // 将初始化类的基类设置成Object Py_INCREF(base); } /* Now the only way base can still be NULL is if type is * &PyBaseObject_Type. */ /* Initialize the base class */ if (base && base->tp_dict == NULL) { // 如果基类的属性列表为空 if (PyType_Ready(base) < 0) // 初始化基类 goto error; } /* Initialize ob_type if NULL. This means extensions that want to be compilable separately on Windows can call PyType_Ready() instead of initializing the ob_type field of their type objects. */ /* The test for base != NULL is really unnecessary, since base is only NULL when type is &PyBaseObject_Type, and we know its ob_type is not NULL (it's initialized to &PyType_Type). But coverity doesn't know that. */ if (type->ob_type == NULL && base != NULL) // 如果初始化类型的类型为空,并且基类不为空 type->ob_type = base->ob_type; // 初始化类型的类型设置成基类的类型 /* Initialize tp_bases */ bases = type->tp_bases; // 获取初始化类型的基类列表 if (bases == NULL) { // 如果基类列表为空 if (base == NULL) // 如果父类为空 bases = PyTuple_New(0); // 基类则生成一个空的元组 else bases = PyTuple_Pack(1, base); // 如果基类不为空,生成长度为1的元组,并将base发入其中 if (bases == NULL) goto error; type->tp_bases = bases; // 将生成的bases设置到初始化类型中 } /* Initialize tp_dict */ dict = type->tp_dict; // 获取类型的属性列表 if (dict == NULL) { dict = PyDict_New(); // 如果属性为空,则生成一个字典,并设置到初始化类型中 if (dict == NULL) goto error; type->tp_dict = dict; } /* Add type-specific descriptors to tp_dict */ if (add_operators(type) < 0) // 给该类型添加描述方法 goto error; if (type->tp_methods != NULL) { if (add_methods(type, type->tp_methods) < 0) // 如果类型方法不为空,则将方法包装后添加到初始化类型中 goto error; } if (type->tp_members != NULL) { if (add_members(type, type->tp_members) < 0) // 如果类型成员不为空,则将成员包装后添加到初始化类型中 goto error; } if (type->tp_getset != NULL) { if (add_getset(type, type->tp_getset) < 0) goto error; } /* Calculate method resolution order */ if (mro_internal(type) < 0) { // 获取初始化类型的基础列表 goto error; } /* Inherit special flags from dominant base */ if (type->tp_base != NULL) inherit_special(type, type->tp_base); // 如果基类不为空,则继承基类的方法属性等 /* Initialize tp_dict properly */ bases = type->tp_mro; // 获取初始化类型的基础列表 assert(bases != NULL); assert(PyTuple_Check(bases)); n = PyTuple_GET_SIZE(bases); for (i = 1; i < n; i++) { PyObject *b = PyTuple_GET_ITEM(bases, i); // 依次获取基础列表的值 if (PyType_Check(b)) inherit_slots(type, (PyTypeObject *)b); // 继承相应的方法 } /* Sanity check for tp_free. */ if (PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) && (type->tp_free == NULL || type->tp_free == PyObject_Del)) { /* This base class needs to call tp_free, but doesn't have * one, or its tp_free is for non-gc'ed objects. */ PyErr_Format(PyExc_TypeError, "type '%.100s' participates in " "gc and is a base type but has inappropriate " "tp_free slot", type->tp_name); goto error; } /* if the type dictionary doesn't contain a __doc__, set it from the tp_doc slot. */ if (PyDict_GetItemString(type->tp_dict, "__doc__") == NULL) { // 设置属性的__doc__属性,如果有设置其中,如果没有则设置为空 if (type->tp_doc != NULL) { PyObject *doc = PyString_FromString(type->tp_doc); if (doc == NULL) goto error; PyDict_SetItemString(type->tp_dict, "__doc__", doc); Py_DECREF(doc); } else { PyDict_SetItemString(type->tp_dict, "__doc__", Py_None); } } /* Some more special stuff */ base = type->tp_base; // 获取基类,如果子类相应的方法为空,则直接将基类方法设置给初始化类型 if (base != NULL) { if (type->tp_as_number == NULL) type->tp_as_number = base->tp_as_number; if (type->tp_as_sequence == NULL) type->tp_as_sequence = base->tp_as_sequence; if (type->tp_as_mapping == NULL) type->tp_as_mapping = base->tp_as_mapping; if (type->tp_as_buffer == NULL) type->tp_as_buffer = base->tp_as_buffer; } /* Link into each base class's list of subclasses */ bases = type->tp_bases; // 获取初始化类型的基类列表, n = PyTuple_GET_SIZE(bases); for (i = 0; i < n; i++) { PyObject *b = PyTuple_GET_ITEM(bases, i); if (PyType_Check(b) && add_subclass((PyTypeObject *)b, type) < 0) // 将初始化类型添加到基类中,填充基类子类列表 goto error; } /* All done -- set the ready flag */ assert(type->tp_dict != NULL); type->tp_flags = (type->tp_flags & ~Py_TPFLAGS_READYING) | Py_TPFLAGS_READY; // 设置该类型已经初始化完成 return 0; error: type->tp_flags &= ~Py_TPFLAGS_READYING; return -1;}

其中,主要经历了五个不中

1.设置type信息,基类及基类列表;
2.填充tp_dict;
3.确定mro;
4.从mro列表继承基类属性;
5.设置基类的子类列表。
其中我们从初始化完成tp_dict后填充方法开始分析;

/* Add type-specific descriptors to tp_dict */    if (add_operators(type) < 0)                               // 给该类型添加描述方法        goto error;    if (type->tp_methods != NULL) {        if (add_methods(type, type->tp_methods) < 0)           // 如果类型方法不为空,则将方法包装后添加到初始化类型中            goto error;    }    if (type->tp_members != NULL) {        if (add_members(type, type->tp_members) < 0)           // 如果类型成员不为空,则将成员包装后添加到初始化类型中            goto error;    }    if (type->tp_getset != NULL) {        if (add_getset(type, type->tp_getset) < 0)                         goto error;    }

查看add_operators(type)方法;

static intadd_operators(PyTypeObject *type){    PyObject *dict = type->tp_dict;    slotdef *p;    PyObject *descr;    void **ptr;    init_slotdefs();                        // 排序slotdefs数组    for (p = slotdefs; p->name; p++) {      // 获取slotdefs的值        if (p->wrapper == NULL)             // 如果包装的函数为空,则直接获取下一个            continue;        ptr = slotptr(type, p->offset);     // 转换获取相应的方法        if (!ptr || !*ptr)            continue;        if (PyDict_GetItem(dict, p->name_strobj))   // 如果在属性列表中获取到相应名称,则不覆盖直接下一个            continue;        descr = PyDescr_NewWrapper(type, p, *ptr);   // 将该方法生成一个新的描述符        if (descr == NULL)            return -1;        if (PyDict_SetItem(dict, p->name_strobj, descr) < 0)   // 将生成的描述符设置到对应的属性列表中            return -1;        Py_DECREF(descr);    }    if (type->tp_new != NULL) {        if (add_tp_new_wrapper(type) < 0)            return -1;    }    return 0;}

这其中出现了init_slotdefs,这在Python中有一个叫slot,一个slot对应一个方法。其定义如下;

typedef struct wrapperbase slotdef;struct wrapperbase {    char *name;                 // 名称    int offset;                 // 偏移,相对于PyHead-TypeObject    void *function;             // 包装的函数    wrapperfunc wrapper;            char *doc;                  // 文档    int flags;            PyObject *name_strobj;     // 对应名称转换为字符串对象};

对于slotdef,提供了多种宏来定义一个slotdef

#define FLSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC, FLAGS) \    {NAME, offsetof(PyTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \     PyDoc_STR(DOC), FLAGS}#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \    {
NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \ PyDoc_STR(DOC)}

两个方法的不同就是一个相对于PyTypeObject,一个相对于PyHeapTypeObject,两个偏移量相对偏移的类型不同。其中PyHeapTypeObject定义如下;

typedef struct _heaptypeobject {    /* Note: there's a dependency on the order of these members       in slotptr() in typeobject.c . */    PyTypeObject ht_type;    PyNumberMethods as_number;    PyMappingMethods as_mapping;    PySequenceMethods as_sequence; /* as_sequence comes after as_mapping,                      so that the mapping wins when both                      the mapping and the sequence define                      a given operator (e.g. __getitem__).                      see add_operators() in typeobject.c . */    PyBufferProcs as_buffer;    PyObject *ht_name, *ht_slots;    /* here are optional user slots, followed by the members. */} PyHeapTypeObject;

为什么会有这个区别是因为在一个PyTypeObject对象中,对应的操作方法比如nb_add是存放在函数指针tp_as_number中,而这是另外一个结构,无法计算出nb_add相对于PyTypeObject的相对位置,由PyHeapTypeObject可以看出,可以直接计算相对位置。

接下来查看下slotdefs数组;

static slotdef slotdefs[] = {    SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,           "x.__len__() <==> len(x)"),    /* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.       The logic in abstract.c always falls back to nb_add/nb_multiply in       this case.  Defining both the nb_* and the sq_* slots to call the       user-defined methods has unexpected side-effects, as shown by       test_descr.notimplemented() */    SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc,          "x.__add__(y) <==> x+y"),    SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc,          "x.__mul__(n) <==> x*n"),    SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc,          "x.__rmul__(n) <==> n*x"),    ...    SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item,           "x.__getitem__(y) <==> x[y]"),    ...    MPSLOT("__getitem__", mp_subscript, slot_mp_subscript,           wrap_binaryfunc,           "x.__getitem__(y) <==> x[y]"),    ...}

其中,可以发现有不同名的操作,也有同名的操作,对应于同名的操作是如何做选择呢,此时如果是PyTypeObject,此时相对于PyHeapTypeObject,其中as_mapping在as_sequence上面,所有mp_subscript的排序会比sq_item靠前,所以会选择mp_subscript。

此时继续查看init_slotdefs代码;

static voidinit_slotdefs(void){    slotdef *p;    static int initialized = 0;    if (initialized)        return;    for (p = slotdefs; p->name; p++) {        p->name_strobj = PyString_InternFromString(p->name);     // 将名称转换为Python内部字符串        if (!p->name_strobj)            Py_FatalError("Out of memory interning slotdef names");    }    qsort((void *)slotdefs, (size_t)(p-slotdefs), sizeof(slotdef),          slotdef_cmp);                                         // 按照slot_cmp函数规则排序    initialized = 1;}

然后执行如下代码;

for (p = slotdefs; p->name; p++) {      // 获取slotdefs的值        if (p->wrapper == NULL)             // 如果包装的函数为空,则直接获取下一个            continue;        ptr = slotptr(type, p->offset);     // 转换获取相应的方法        if (!ptr || !*ptr)            continue;        if (PyDict_GetItem(dict, p->name_strobj))   // 如果在属性列表中获取到相应名称,则不覆盖直接下一个            continue;        descr = PyDescr_NewWrapper(type, p, *ptr);   // 将该方法生成一个新的描述符        if (descr == NULL)            return -1;        if (PyDict_SetItem(dict, p->name_strobj, descr) < 0)   // 将生成的描述符设置到对应的属性列表中            return -1;

这里可以看到当需要在tp_dict中设置属性时,并不是直接向slotdef直接设置到属性中,而是通过PyDescr_NewWrapper生成一个描述符放置在tp_dict中,为何要这么设计呢?

我们先查看PyDescr_NewWrapper;

PyObject *PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *base, void *wrapped){    PyWrapperDescrObject *descr;    descr = (PyWrapperDescrObject *)descr_new(&PyWrapperDescr_Type,                         type, base->name);    if (descr != NULL) {        descr->d_base = base;                // slotdef         descr->d_wrapped = wrapped;          // 被包装的函数    }    return (PyObject *)descr;}

继续查看descr_new;

static PyDescrObject *descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name){    PyDescrObject *descr;    descr = (PyDescrObject *)PyType_GenericAlloc(descrtype, 0);    if (descr != NULL) {        Py_XINCREF(type);        descr->d_type = type;        // 设置装饰的类型        descr->d_name = PyString_InternFromString(name);  // 设置被装饰的名称        if (descr->d_name == NULL) {            Py_DECREF(descr);            descr = NULL;        }    }    return descr;}

由此查看PyDescrObject类型;

#define PyDescr_COMMON \    PyObject_HEAD \    PyTypeObject *d_type; \    PyObject *d_nametypedef struct {    PyDescr_COMMON;    struct wrapperbase *d_base;    void *d_wrapped; /* This can be any function pointer */} PyWrapperDescrObject;

由此可知一个descr是一个type类型的对象,为什么要转换为一个type类型呢,当调用tp_dict的方法时,需要调用其中tp_call方法,而slotdef并不是一个可调用对象,并不符合可调用的要求。

在以上流程中,比较重要的就是怎样获取对应的ptr然后生成wrapper存入tp_dict中,

ptr = slotptr(type, p->offset);     // 转换获取相应的方法

对应的方法为

static void **slotptr(PyTypeObject *type, int ioffset){    char *ptr;    long offset = ioffset;    /* Note: this depends on the order of the members of PyHeapTypeObject! */    assert(offset >= 0);    assert((size_t)offset < offsetof(PyHeapTypeObject, as_buffer));    if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {        ptr = (char *)type->tp_as_sequence;        offset -= offsetof(PyHeapTypeObject, as_sequence);    }    else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_mapping)) {        ptr = (char *)type->tp_as_mapping;        offset -= offsetof(PyHeapTypeObject, as_mapping);    }    else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_number)) {        ptr = (char *)type->tp_as_number;        offset -= offsetof(PyHeapTypeObject, as_number);    }    else {        ptr = (char *)type;    }    if (ptr != NULL)        ptr += offset;    return (void **)ptr;}

此时,从上至下,依次是PyHeapTypeObject对应的as_sequence,as_mapping,as_number,这是因为offset的偏移是从大到小进行检查,因为as_sequence的距离是最远的,如果比as_sequence还小则再检查as_mapping,如果比as_mapping还小则检查as_number,这样就可以查找出对应偏移量对应的方法。

此时tp_dict属性字典方法填充完成。
当tp_dict完成后类继承相关的操作
由于Python中的类的基础是一句传入的参数依次从左至右开始继承;

>>> class A(object):...     pass... >>> class B(object):...     pass... >>> A.__mro__(
,
)>>> class C(B,A):... pass... >>> class D(C,A):... pass... >>> D.__mro__(
,
,
,
,
)>>> D.__bases__(
,
)>>> C.__mro__(
,
,
,
)>>>

可见D的继承顺序是从左至右,依次继承初始化,

继承的代码执行流程如下;

/* Initialize tp_dict properly */    bases = type->tp_mro;                                      // 获取初始化类型的基础列表    assert(bases != NULL);        assert(PyTuple_Check(bases));    n = PyTuple_GET_SIZE(bases);    for (i = 1; i < n; i++) {        PyObject *b = PyTuple_GET_ITEM(bases, i);              // 依次获取基础列表的值        if (PyType_Check(b))            inherit_slots(type, (PyTypeObject *)b);            // 继承相应的方法    }

由例子可知,tp_mro的第一项是自身,所以i=1开始,依次获取对应的基类,然后执行,inherit_slots,该函数主要就是检查基类中对应的方法子类中是否拥有,如果没有拥有则拷贝到子类中对应的方法中去,以此达到继承父类方法的功能,主要是一些方法的拷贝有兴趣可自行查看。

接着就将子类添加到基类的子类列表中

bases = type->tp_bases;                                    // 获取初始化类型的基类列表,    n = PyTuple_GET_SIZE(bases);    for (i = 0; i < n; i++) {        PyObject *b = PyTuple_GET_ITEM(bases, i);                    if (PyType_Check(b) &&            add_subclass((PyTypeObject *)b, type) < 0)         // 将初始化类型添加到基类中,填充基类子类列表            goto error;    }

其中调用add_subclass时还生成了Python中的引用相关的操作,有兴趣课自行查看。

至此初始化就完成,一个PyTypeObject的初始化工作就已经完成,初始化内置类型的操作流程基本分析完成。

转载地址:https://blog.csdn.net/qq_33339479/article/details/79839503 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Python源码学习:Python类机制分析-用户自定义类
下一篇:Python源码学习:Python函数浅析-函数闭包

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年03月06日 17时56分14秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

php中带?错误,参考-此错误在PHP中意味着什么? 2019-04-21
php生成链接列表,根据URL链接和抛文本生成链接<a>标签的PHP函数 2019-04-21
matlab里inline定义矩阵,Matlab中的inline函数_matlab中inline函数 2019-04-21
php html标签自定义属性,浅谈JS读取DOM对象(标签)的自定义属性 2019-04-21
如何使用matlab的siso,利用Matlab内建程式SISODesignTool完成系统分析(Matlab61)开启.PDF... 2019-04-21
php 实现 model层,Thinkhphp5控制器调用的Model层的方法总结 2019-04-21
matlab6.0序列号,MFC软件获取USB设备的制造商、产品、序列号 2019-04-21
matlab中多边形滤波器,几种常见空间滤波器MATLAB实现 2019-04-21
matlab fminimax 例子,Matlab应用实例(8)—fminimax 2019-04-21
php://filter利用条件,浅谈php://filter技巧 2019-04-21
mplayer-php,mplayer+smplayer 前后端播放器安装 2019-04-21
oracle昨日时间,。。今日,昨日,上周,本月,本年,按时间统计总金额 2019-04-21
php验证卡号,PHP验证信用卡卡号是否正确函数 2019-04-21
mpvue微信小程序动画_推荐两个微信小程序开发框架 2019-04-21
固态硬盘分为哪几种_零基础玩转固态硬盘 深度排雷 买SSD掌握这些就够了 2019-04-21
调python返回图片_Python异常处理,3个好习惯分享给你 2019-04-21
15拆解_收藏:15款劲芯微芯片无线充产品拆解 2019-04-21
弹出u盘_都说:U盘直接拔出不会丢失文件,“安全弹出”形同虚设,对吗? 2019-04-21
怎么查看elementui版本_2021新年 Vue3.0 + Element UI 尝鲜小记 2019-04-21
adreno630gpu参数_小米8搭载Adreno 630图形处理器 比荣耀play上的GPU Turbo更成熟 2019-04-21