Python3.5源码剖析 | 字符串对象创建

 首先需要明白,在Python3.5的源码里,我们操作的文本字符串是以unicode的形式存在的。

    如果字符串中仅仅包含ASCII,那么会通过c文件中的PyUnicode_New函数进行创建,创建的结构体是PyASCIIObject,该结构体的内容如下:

typedef struct {      PyObject_HEAD    Py_ssize_t length;          /* Number of code points in the string */    Py_hash_t hash;             /* Hash value; -1 if not set */    struct {        unsigned int interned:2;        unsigned int kind:3;        unsigned int compact:1;        unsigned int ascii:1;        unsigned int ready:1;        unsigned int :24;    } state;    wchar_t *wstr;              /* wchar_t representation (null-terminated) */} PyASCIIObject;

    仅ASCII的字符串初始化会把state中的ascii和compact都进行设置,其中的ascii用于表明字符串是否是纯ASCII,compact用于表明是否是紧凑型字符串,如果是紧凑型字符串,会仅申请一块内容地址用来存放结构和和数据(结构体后紧挨着数据),非紧凑型会申请两块内存,分别存放结构体和数据。

    这个结构体继承了PyObject_HEAD,那说明是一个定长Object(Python中一切皆对象,都是PyObject),即大小在没初始化之前就确定了,很显然,每个ASCII字符就是一个字节,而length是我们定义的字符串的长度,hash是计算的字符串哈希值(因为Python的字符串是定长不可变对象)。

    在state结构体当中,interned用于字符串的优化缓冲机制,即对于特定的字符串,如果重复出现,只会指向interned字典当中已经存在的字符串地址,这样面对相同字符串的时候就不需要重复申请地址了,有点像C++中的移动语义,直接接管过来,当然这里还涉及了一些引用计数的问题,下回再讲。

    kind则用于表明字符串的类型,目前就只有4种类型,宽字节(2字节或者4字节,由平台决定),1字节,2字节和4字节。

    ready用于表明字符串对象布局是否初始化完整了,我也不太了解这个字段的用途,但是根据C文件的代码来看,只要内存申请完毕,就会被设置为1。

    最后一个字段无名,用于保证PyUnicode_DATA()被填充为4字节。

    wstr是一个不包含结束标志\0的字符指针。

    如果是一个非ASCII字符串,会使用PyCompactUnicodeObject置入PyUnicode_New中进行初始化,结构如下:

typedef struct {    PyASCIIObject _base;    Py_ssize_t utf8_length;     /* Number of bytes in utf8, excluding the                                 * terminating \0. */    char *utf8;                 /* UTF-8 representation (null-terminated) */    Py_ssize_t wstr_length;     /* Number of code points in wstr, possible                                 * surrogates count as two code points. */} PyCompactUnicodeObject;

    它继承自ASCII结构体,utf8_length用于记录uft8字段长度(包括结束符\0),utf8字符指针表示无结束符的字符串,wstr_length是宽字符的长度。

    如果字符串使用PyUnicodeObject结构体置入PyUnicode_FromUnicode中进行初始化,那么就会存在两块内存地址,分别存放结构体和数据。​​​​​​​

typedef struct {    PyCompactUnicodeObject _base;    union {        void *any;        Py_UCS1 *latin1;        Py_UCS2 *ucs2;        Py_UCS4 *ucs4;    } data;                     /* Canonical, smallest-form Unicode buffer */} PyUnicodeObject;

    其中多了一个联合体,联合体内部的成员公用一块内存地址,这里自然是4个字节,可以方便进行1,2,4字节的数据存储和转化。

    综上可以得知,虽然Python3.5的字符串对象都是unicode,但是为了节省内存,并非每一个字符都是4字节,而是针对0-255设置为1字节,256-65535设置为2字节,之后才是4字节。

    这里我们分析第一个ASCII字符串的初始化,采用PyUnicode_New和PyASCIIObject结构体。

PyObject * PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar){    PyObject *obj;    PyCompactUnicodeObject *unicode;    void *data;    enum PyUnicode_Kind kind;    int is_sharing, is_ascii;    Py_ssize_t char_size;    Py_ssize_t struct_size;    /* 优化空字符串 */    //如果是空字符串的话,会直接引用全局的unicode_empty对象    if (size == 0 && unicode_empty != NULL) {        Py_INCREF(unicode_empty);        return unicode_empty;    }    is_ascii = 0;    is_sharing = 0;    //计算ASCII结构体大小    struct_size = sizeof(PyCompactUnicodeObject);        //分别用于1,2,4字节大小的内存分配    if (maxchar < 128) {        kind = PyUnicode_1BYTE_KIND;        char_size = 1;        is_ascii = 1;        struct_size = sizeof(PyASCIIObject);    }    else if (maxchar < 256) {        kind = PyUnicode_1BYTE_KIND;        char_size = 1;    }    else if (maxchar < 65536) {        kind = PyUnicode_2BYTE_KIND;        char_size = 2;        if (sizeof(wchar_t) == 2)            is_sharing = 1;    }    else {        if (maxchar > MAX_UNICODE) {            PyErr_SetString(PyExc_SystemError,           "invalid maximum character passed to PyUnicode_New");            return NULL;        }        kind = PyUnicode_4BYTE_KIND;        char_size = 4;        if (sizeof(wchar_t) == 4)            is_sharing = 1;    }    /* 防止内存越界 */    if (size < 0) {        PyErr_SetString(PyExc_SystemError,                        "Negative size passed to PyUnicode_New");        return NULL;    }    if (size > ((PY_SSIZE_T_MAX - struct_size) / char_size - 1))        return PyErr_NoMemory();    /*     这里的内存分配很简单,就是申请了结构体+字符串(有结束符)大小的内存     */    obj = (PyObject *) PyObject_MALLOC(struct_size + (size + 1) * char_size);    if (obj == NULL)        return PyErr_NoMemory();    obj = PyObject_INIT(obj, &PyUnicode_Type);    if (obj == NULL)        return NULL;    //申请内存后将内存指针转化    unicode = (PyCompactUnicodeObject *)obj;    if (is_ascii)        data = ((PyASCIIObject*)obj) + 1;    else        data = unicode + 1;    //对结构体参数初始化    _PyUnicode_LENGTH(unicode) = size;    _PyUnicode_HASH(unicode) = -1;    _PyUnicode_STATE(unicode).interned = 0;    _PyUnicode_STATE(unicode).kind = kind;    _PyUnicode_STATE(unicode).compact = 1;    _PyUnicode_STATE(unicode).ready = 1;    _PyUnicode_STATE(unicode).ascii = is_ascii;        //如果是ASSCII,把结尾设置为0,把宽字符设置为空    if (is_ascii) {        ((char*)data)[size] = 0;        _PyUnicode_WSTR(unicode) = NULL;    }    else if (kind == PyUnicode_1BYTE_KIND) {        ((char*)data)[size] = 0;        _PyUnicode_WSTR(unicode) = NULL;        _PyUnicode_WSTR_LENGTH(unicode) = 0;        unicode->utf8 = NULL;        unicode->utf8_length = 0;    }    else {        unicode->utf8 = NULL;        unicode->utf8_length = 0;        if (kind == PyUnicode_2BYTE_KIND)            ((Py_UCS2*)data)[size] = 0;        else /* kind == PyUnicode_4BYTE_KIND */            ((Py_UCS4*)data)[size] = 0;        if (is_sharing) {            _PyUnicode_WSTR_LENGTH(unicode) = size;            _PyUnicode_WSTR(unicode) = (wchar_t *)data;        }        else {            _PyUnicode_WSTR_LENGTH(unicode) = 0;            _PyUnicode_WSTR(unicode) = NULL;        }    }#ifdef Py_DEBUG    unicode_fill_invalid((PyObject*)unicode, 0);#endif    assert(_PyUnicode_CheckConsistency((PyObject*)unicode, 0));    return obj;}

    这里实际上返回的是obj,而不是unicode,所以做的工作其实就是分配一块指向数据的内存,而unicode是用来做安全审查工作和初始化结构体的。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值