设为首页 收藏本站
查看: 1073|回复: 0

[经验分享] [Python源码学习]之对象创建与销毁

[复制链接]

尚未签到

发表于 2017-5-5 11:36:42 | 显示全部楼层 |阅读模式
接前面Python源码笔记之内存管理,尝试看看Python的对象的创建与销毁。Python的对象类型还挺多,在Python源码笔记之数据类型中试图列一个表出来,最终未果。
不敢贪多,看4个内建对象。创建对象,也就是创建下面几个结构体的实例了:




结构体



通用C API?



Type中的tp_new




整数



PyLongObject



_PyLong_New()



long_new




字符串



PyUnicodeObject



_PyUnicode_New()



unicode_new




列表



PyListObject



PyList_New



PyType_GenericNew




字典



PyDictObject



PyDict_New()



dict_new






如何创建?



封装的层次太多,太让人迷惑了。简单说,不就是malloc一块合适大小的内存,让PyXxxxObject的指针指向它,为成员赋值么?
如何分配内存:


  • 最底层是 C 中的 malloc、realloc、free 这3个东西


  • Python提供简单的封装PyMem_{Malloc、Realloc、Free}这3个东西



  • 高级的内存接口PyObject_{Malloc、Free}(内存池) 等



  • 含有类型信息(知道结构体大小)的PyObject_{New、NewVar}等



  • 带垃圾回收信息的_PyObject_GC_{Malloc、New、NewVar、Del}等


幸好,创建内建类型时,我们不需要这些东西,因为它们提供了




  • _PyLong_New()



  • _PyUnicode_New()



  • PyList_New



  • PyDict_New()

  • ...

以下划线开头的啥意思,看来是不希望大家用的东西,特别是_PyUnicode_New(),直接就是一个static函数。看来要创建一个整数或字符串,需要使用其他的东西了,这就是:

PyLong_FromLong(long ival)
PyLong_FromUnsignedLong(unsigned long ival)
PyLong_FromDouble(double dval)
PyLong_FromVoidPtr(void *p)
PyLong_FromLongLong(PY_LONG_LONG ival)
...
PyObject *PyUnicode_FromString(const char *u)
PyObject *PyUnicode_FromUnicode(const Py_UNICODE *u, Py_ssize_t size)
...


PyXxxx_Type



除了前面这种方式外,Python提供了一种"统一"的创建接口,通过 PyXxxx_Type 中的那些函数指针(fixme)。




PyLong_Type



PyUnicode_Type



PyList_Type



PyDict_Type




tp_base



0



&PyBaseObject_Type



0



0




tp_init



0



0



list_init



dict_init




tp_alloc



0



0



PyType_GenericAlloc



PyType_GenericAlloc




tp_new



long_new



unicode_new



PyType_GenericNew



dict_new




tp_free



PyObject_Del



PyObject_Del



PyObject_GC_Del



PyObject_GC_Del





真复杂,静下心,试着读读看(每个都是从tp_new开始):


  • 整数 long_new

这个应该是比较简单,它调用的基本上就是PyLong_From***这些东西


  • 字符串 unicode_new

调用基本上就是_PyUnicode_New和PyObject_Str(x)这些
只是它设置了tp_base,不清楚何用。




  • 列表PyType_GenericNew

该函数源码如下:

PyObject *
PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    return type->tp_alloc(type, 0);
}

恩,看起来很简单,直接调用了 tp_alloc。也就是PyType_GenericAlloc
源码:

PyObject *
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
{
    PyObject *obj;
    const size_t size = _PyObject_VAR_SIZE(type, nitems+1);

    if (PyType_IS_GC(type))
        obj = _PyObject_GC_Malloc(size);
    else
        obj = (PyObject *)PyObject_MALLOC(size);
...

视情况调用_PyObject_GC_Malloc或PyObject_MALLOC,对list,当然是前者喽。


  • 字典 dict_new

额,和PyDict_New()类似,只是,还是不清楚tp_alloc干嘛用的。


删除



Python中每一个对象都有一个引用计数。当引用计数为零时,对象将被删除。



Py_REFCNT(ob)




3个简单的宏,对应结构体3个成员




Py_TYPE(ob)





Py_SIZE(ob)





_Py_NewReference(op)




将引用计数设置为1




Py_INCREF(op)




增加、减少引用计数,带X的会判断op是否为空




Py_XINCREF(op)





Py_DECREF(op)





Py_XDECREF(op)





_Py_Dealloc(op)




引用计数降为零时会被调用,进而调用对象类型中的tp_dealloc





每一个类型都提供有自己的tp_dealloc,比如:unicode_dealloc、long_dealloc、list_dealloc、dict_dealloc
但尚不清楚这些东西和 tp_free 是什么关系,如何关联的。(要学的东西好多啊)
和删除有关的,应该还有两个问题:


  • 缓存?比如小整数始终被缓存起来,永生不灭。但是



static void
long_dealloc(PyObject *v)
{
    Py_TYPE(v)->tp_free(v);
}

恩,只要保证引用计数不为0就行了




  • 循环引用? list 和 dict 都可能会循环引用,所以和整数处理不同。创建和销毁都是用的带 GC 的东西:PyObject_GC_***


结构体定义



似乎没什么关系哈,不是还是附上




  • PyLongObject、PyUnicodeObject、PyListObject、PyDictObject 4个结构体的定义:



  • 整数对象 PyLongObject




struct _longobject {
        PyObject_VAR_HEAD
        digit ob_digit[1];
};
typedef struct _longobject PyLongObject;




  • 字符串对象 PyUnicodeObject



typedef struct {
    PyObject_HEAD
    Py_ssize_t length;          /* Length of raw Unicode data in buffer */
    Py_UNICODE *str;            /* Raw Unicode buffer */
    Py_hash_t hash;             /* Hash value; -1 if not set */
    int state;                  /* != 0 if interned. In this case the two
                                 * references from the dictionary to this object
                                 * are *not* counted in ob_refcnt. */
    PyObject *defenc;           /* (Default) Encoded version as Python
                                   string, or NULL; this is used for
                                   implementing the buffer protocol */
} PyUnicodeObject;




  • 列表对象 PyListObject



typedef struct {
    PyObject_VAR_HEAD
    /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
    PyObject **ob_item;

    /* ob_item contains space for 'allocated' elements.  The number
     * currently in use is ob_size.
     * Invariants:
     *     0 <= ob_size <= allocated
     *     len(list) == ob_size
     *     ob_item == NULL implies ob_size == allocated == 0
     * list.sort() temporarily sets allocated to -1 to detect mutations.
     *
     * Items must normally not be NULL, except during construction when
     * the list is not yet visible outside the function that builds it.
     */
    Py_ssize_t allocated;
} PyListObject;




  • 字典对象 PyDictObject



typedef struct _dictobject PyDictObject;
struct _dictobject {
    PyObject_HEAD
    Py_ssize_t ma_fill;  /* # Active + # Dummy */
    Py_ssize_t ma_used;  /* # Active */

    /* The table contains ma_mask + 1 slots, and that's a power of 2.
     * We store the mask instead of the size because the mask is more
     * frequently needed.
     */
    Py_ssize_t ma_mask;

    /* ma_table points to ma_smalltable for small tables, else to
     * additional malloc'ed memory.  ma_table is never NULL!  This rule
     * saves repeated runtime null-tests in the workhorse getitem and
     * setitem calls.
     */
    PyDictEntry *ma_table;
    PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, Py_hash_t hash);
    PyDictEntry ma_smalltable[PyDict_MINSIZE];
};


object.h中的几个宏



简单罗列一下(只为方便自己查看)




  • 3个宏用来获取PyObject/PyVarObject结构体中的成员



#define Py_REFCNT(ob)           (((PyObject*)(ob))->ob_refcnt)
#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)
#define Py_SIZE(ob)             (((PyVarObject*)(ob))->ob_size)


  • 1个宏用来将引用计数设置为1。



#define _Py_NewReference(op) (Py_REFCNT(op) = 1)


  • 4个宏用来增加/减小 引用计数。(带X的会判断对象指针为否为空)



#define Py_INCREF(op) (((PyObject*)(op))->ob_refcnt++)
#define Py_DECREF(op)                                   \
    do {                                                \
        if ( --((PyObject*)(op))->ob_refcnt != 0)       \
            ;                                           \
        else                                            \
        _Py_Dealloc((PyObject *)(op));                  \
    } while (0)
#define Py_CLEAR(op)                            \
    do {                                        \
        if (op) {                               \
            PyObject *_py_tmp = (PyObject *)(op);               \
            (op) = NULL;                        \
            Py_DECREF(_py_tmp);                 \
        }                                       \
    } while (0)

#define Py_XINCREF(op) do { if ((op) == NULL) ; else Py_INCREF(op); } while (0)
#define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0)


  • 引用计数为零时调用的宏,进而调用对象类型中的 tp_dealloc!



#define _Py_Dealloc(op) ((*Py_TYPE(op)->tp_dealloc)((PyObject *)(op)))

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-373429-1-1.html 上篇帖子: python + lxml 抓取网页 ,不需用正则,用xpath 下篇帖子: Python处理MLDonkey 下载中文文件乱码问题 (2)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表