python源码分析----对象结构

虽然自己会吐槽python这语言让人受不了的慢,不过也不得不说python语言在语法上的精炼,在写代码的时候确实会比java方便一些。。。不过由于python无法像java一样项目的开始先定义一套强类型限制的接口,所以总感觉在写python的时候有点不踏实。。。不过pycharm从某种程度上通过注释也能减轻一点这方面的担忧。。

好了。。。。。决定了要看一下python的实现。。。这里就先从python的对象结构来说吧。。。。

在python中,所有的东西都是对象,整数是,方法也是,。。总之什么都是。。。。

在看python的代码实现中,可以随处看到指针类型:PyObject*,那么我们就先来看看PyObject这到底是怎么定义的吧:

typedef struct _object {  //这个是所有python对象的基础,这个是不可变对象
    PyObject_HEAD
} PyObject;

他就是整个python环境中最上层的对象结构,很简单,就包括一个对象头部,那么来看看它的定义:

//通过这个结构,将所有对象构成一个双链表
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA                struct _object *_ob_next;               struct _object *_ob_prev;

#define _PyObject_EXTRA_INIT 0, 0,

#else
#define _PyObject_HEAD_EXTRA
#define _PyObject_EXTRA_INIT
#endif

//所有对象共有的数据,这个放在所有对象内存的最开始部分,首先是构成对象链表的前后执指针,其次是引用数量,以及它的类型对象
/**
 (1)前后指针,构成循环链表
 (2)引用计数
 (3)类型对象
**/
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD                       _PyObject_HEAD_EXTRA                    Py_ssize_t ob_refcnt;                   struct _typeobject *ob_type;

可以看出,头部由3部分来构成,首先是一个前后指针,用于将对象构成一个双向的链表,其次就是比较重要的引用计数了。。。然后还有就是类型对象----》每一个对象都有一个指针指向其所指向的类型对象。。。

既然看到了引用计数,那么就来说说python的GC吧。。。它跟java在GC最大的不同点就是有一套基于引用计数的内存回收。。。

(1)每一个对象都关联一个引用计数,当这个计数为0的时候,那么它的内存就可以被回收了

(2)对于有循环引用的情况,python也有一套可达性分析的gc来回收内存。。当然它这部分就比java简单的多了

上面看的时最基础的PyObject结构。。。另外这里还有另外一种结构:PyVarObject,它用于指代一些变长对象,主要是一些容器。。。。

//一般都是一些容器啥的
typedef struct {        //可变对象的基础
    PyObject_VAR_HEAD
} PyVarObject;

 //即使是可变对象,首先也是有一个统一的对象头部,ob_size一般用于记录容器中数据项个数,注意这里不是字节数
#define PyObject_VAR_HEAD                   PyObject_HEAD                           Py_ssize_t ob_size; /* Number of items in variable part */
#define Py_INVALID_SIZE (Py_ssize_t)-1

它其实也就是扩展了一个0b_size字段。。。本质上也是与PyObject统一的。。。。所以对于PyVarObject,也可以用PyObject指针来引用。。从而达到了统一。。。。

通过上面的内容,我们知道,所有的对象,都会有一个ob_type指针域,用于指向当前对象的类型对象,那么接下来来看看类型对象的定义:

//这个用来指定一个对象的类型对象
typedef struct _typeobject {
    PyObject_VAR_HEAD       //这里有一个变长对象头部
    const char *tp_name; /* For printing, in format "<module>.<name>" */  //用于打印当前类型的信息
    Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */   //用于分配的大小

    /* Methods to implement standard operations */
    //下面是标准方法的定义
    destructor tp_dealloc;    //析构当前对象
    printfunc tp_print;       //在调用print打印类型的时候,将会调用这里来进行输出
    getattrfunc tp_getattr;   //getter函数
    setattrfunc tp_setattr;   //setter函数
    cmpfunc tp_compare;       //比较函数
    reprfunc tp_repr;         //转化为字符串

    /* Method suites for standard classes */

    PyNumberMethods *tp_as_number;      //最为数字的进行操作的函数
    PySequenceMethods *tp_as_sequence;  //作为序列进行操作的函数
    PyMappingMethods *tp_as_mapping;    //作为字典进行操作的函数

    /* More standard operations (here for binary compatibility) */

    hashfunc tp_hash;                   //获取hash值的函数
    ternaryfunc tp_call;                //作为方法进行调用的时候
    reprfunc tp_str;
    getattrofunc tp_getattro;
    setattrofunc tp_setattro;

    /* Functions to access object as input/output buffer */
    PyBufferProcs *tp_as_buffer;       //将当前对象作为buffer进行处理的时候的方法

    /* Flags to define presence of optional/expanded features */
    long tp_flags;

    const char *tp_doc; /* Documentation string */   //当前类型的文档

    /* Assigned meaning in release 2.0 */
    /* call function for all accessible objects */
    traverseproc tp_traverse;

    /* delete references to contained objects */
    inquiry tp_clear;                            

    /* Assigned meaning in release 2.1 */
    /* rich comparisons */
    richcmpfunc tp_richcompare;

    /* weak reference enabler */
    Py_ssize_t tp_weaklistoffset;

    /* Added in release 2.2 */
    /* Iterators */
    getiterfunc tp_iter;
    iternextfunc tp_iternext;

    /* Attribute descriptor and subclassing stuff */
    struct PyMethodDef *tp_methods;          //方法
    struct PyMemberDef *tp_members;          //成员属性
    struct PyGetSetDef *tp_getset;
    struct _typeobject *tp_base;             //父类型
    PyObject *tp_dict;
    descrgetfunc tp_descr_get;
    descrsetfunc tp_descr_set;
    Py_ssize_t tp_dictoffset;
    initproc tp_init;
    allocfunc tp_alloc;
    newfunc tp_new;
    freefunc tp_free; /* Low-level free-memory routine */
    inquiry tp_is_gc; /* For PyObject_IS_GC */
    PyObject *tp_bases;
    PyObject *tp_mro; /* method resolution order */
    PyObject *tp_cache;
    PyObject *tp_subclasses;
    PyObject *tp_weaklist;
    destructor tp_del;

    /* Type attribute cache version tag. Added in version 2.6 */
    unsigned int tp_version_tag;

#ifdef COUNT_ALLOCS
    /* these must be last and never explicitly initialized */
    Py_ssize_t tp_allocs;
    Py_ssize_t tp_frees;
    Py_ssize_t tp_maxalloc;
    struct _typeobject *tp_prev;
    struct _typeobject *tp_next;
#endif
} PyTypeObject;

可以看到,这里主要是定义了很多公共的函数。。。。同时这里可以看到最开始他也定义了一个变长对象的头部PyObject_VAR_HEAD

那么也就是说,类型对象本身也是一个对象,也可以用PyObject指针来引用。。。。

嗯。。那么。。这里问题就来了。。。。既然类型对象也是一个对象。。。。那么类型对象他自己的类型对象是啥呢。。。?

嗯。。。在python中定义了一个最顶层的类型对象。。。。。

//最顶层的类型对象,它进行一个类型对象的自引用
PyTypeObject PyType_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "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 */
    0,                                  /* 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 */
    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 | Py_TPFLAGS_TYPE_SUBCLASS,         /* tp_flags */
    type_doc,                                   /* tp_doc */
    (traverseproc)type_traverse,                /* tp_traverse */
    (inquiry)type_clear,                        /* tp_clear */
    type_richcompare,                                           /* 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 */
    type_init,                                  /* tp_init */
    0,                                          /* tp_alloc */
    type_new,                                   /* tp_new */
    PyObject_GC_Del,                            /* tp_free */
    (inquiry)type_is_gc,                        /* tp_is_gc */
};

这里可以看到它的类型对象本身引用了自己。。。。。

以后可以看到,所有的python类型对象都将它自己的类型对象指向了PyType_Type

好了。。基本上比较粗略的了解了python的对象结构。。。。。

时间: 2024-10-12 11:49:51

python源码分析----对象结构的相关文章

leveldb源码分析--Key结构

[注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念.这篇文章本来应该顺理成章的介绍Batch相关流程和结构了,但是Batch涉及到了一些编码和Key相关的概念,所以我觉得应该先理清这方面的概念有助于大家更容易理解后面的内容. 在dbformat.h/cc文件中我们首先看到的是 typedef uint64_t SequenceNumber; str

Python源码--整数对象(PyIntObject)的内存池

[背景] 原文链接:http://blog.csdn.net/ordeder/article/details/25343633 Python整数对象是不可变对象,什么意思呢?例如执行如下python语句 >>>a = 1023 >>>a = 1024 >>>b = a >>>c = 1024 >>>d = 195 >>>e = 195 python的整数对象结构为: typedef struct {

Python源码分析(一)

最近想学习下Python的源码,希望写个系列博客,记录的同时督促自己学习. Python源码目录 从Python.org中下载源代码压缩包并解压,我下载的是Python2.7.12,解压后: 对于主要的文件夹做出介绍: Include:包含Python提供的所有头文件,如果需要自己使用C或者C++编写自定义模块扩展Python,就需要用到这里的头文件: Lib: 包含Python自带的所有标准库,全部由Python语言编写: Modules:包含了所有使用C语言编写的模块: Parser:Pyt

zepto源码分析-代码结构【转载】

本来想学习一下jQuery的源码,但由于jQuery的源码有10000多行,设计相当复杂,所以决定从zepto开始,分析一个成熟的框架的代码结构及执行步骤. 网上也有很多zepto的源码分析,有的给源码添加注释,有的谈与jQuery的不同,但是都没有系统的讲解zepto框架的代码结构及初始化Zepto对象的过程. 准备 默认你已经对面向对象有一定的了解,本文是边实践边写的,虽有些乱,但好处是为大家提供了分析的思路. 英文文档. 中文文档 注意在文中$变量表示一个函数对象,而$()表示执行函数,他

jquery源码分析(二)——结构

再来复习下整体架构: jQuery源码分析(基于 jQuery 1.11 版本,共计8829行源码) (21,94)                定义了一些变量和函数jQuery=function(){} (96,280)        给jQuery添加一些方法和属性,jQuery.fn=jQuery.prototype(285,347)        extend:        jQuery的一些继承方法        更容易进行后续的扩展                       

[python 源码]字符串对象的实现

还是带着问题上路吧,和整数对象的实现同样的问题: >>> a='abc' >>> b='abc' >>> a is b True >>> c='abc'*10 >>> d='abc'*10 >>> d is c False why?在整数对象的实现中,对待小整数有小整数对象池,对待大整数对申请内存,字符串对象的实验也是这样的吗??? NO 先看下字符串对象的定义: typedef struct{ P

[python 源码]整数对象的创建和维护

刚开始学python时候,发现一个很迷惑的现象,一直到看了源码后才知道了: >>> a=6 >>> b=6 >>> a is b True 想用同样的参数初始化两个对象,结果却是,这两个对象其实是同样的对象????逗我呢? >>> a=666 >>> b=666 >>> a is b False 这又是怎么回事?为什么现在又是False了??? 这些不同,主要来自于python中对待小整数和大整数的

python源码分析----内存分配(1)

上面的一篇粗略的介绍了一下python的对象结构,这篇来分析一个非常重要的部分,内存分配... 好像自己看的源代码,只要是跟C语言相关的,都在内存处理方面做了相当多的工作....例如nginx,它也有实现自己的pool,python当然也不例外.... python在内存分配上面分成了4个层次吧... _____   ______   ______       ________ [ int ] [ dict ] [ list ] ... [ string ]       Python core

Python源码分析之dis

一.简单例子 def add(a, b): return a + b add_nums.py import foo a = [1, 'python'] a = 'a string' def func(): a = 1 b = 257 print(a + b) print(a) if __name__ == '__main__': func() foo.add(1, 2) demo.py 运行python demo.py 在Python3 中,会自动生成一个 __pycache__ 文件夹,.py