第3篇:CPython內(nèi)部探究:PyASCIIObject的初始化

在CPython3.3之后,字符串對(duì)象發(fā)生了根本性的變法,本篇我們來(lái)討論一下字符串對(duì)象,在Include/unicodeobject.h,在整個(gè)源代碼的官方文檔可以歸納出幾點(diǎn)绿渣。在CPython3.3+之后,Unicode字符串分為有4種

緊湊型ASCII(Compact ASCII)

緊湊型ASCII也稱為ASCII限定字符串(ASCII only String).其對(duì)應(yīng)PyASCIIObject結(jié)構(gòu)體电湘,該對(duì)象使用一個(gè)空間連續(xù)的內(nèi)存塊(一個(gè)內(nèi)部的state結(jié)構(gòu)體和一個(gè)wchar_t類型的指針)狡门,緊湊型ASCII只能涵蓋拉丁編碼以內(nèi)的字符舞竿。ASCII字符限定意味著PyASCIIObject只能U+0000 ~ U+007F這段區(qū)間的字符碼。

typedef struct {
    PyObject_HEAD
    Py_ssize_t length;          /* 字符串中的碼位個(gè)數(shù) */
    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;              /*C底層的寬字符序列以NUL結(jié)束*/
} PyASCIIObject;

ASCII限定字符串可以由PyUnicode_New函數(shù)使用其結(jié)構(gòu)體創(chuàng)建并設(shè)定state.ascii為1,state.compact為1奉芦。

從上面的類定義可知

  • length用于保存字符串中字符編碼的數(shù)量
  • hash用于緩存C級(jí)別字符串的哈系值赵抢。由于字符串對(duì)象是不可變對(duì)象,這樣避免每次重新計(jì)算該字符串的hash字段的值
  • state保存了保存了其子類實(shí)例的狀態(tài)信息,
  • wstr是緩存C字符串的一個(gè)wchar指針声功,當(dāng)然它是以“\0”結(jié)束

緊湊型Unicode(Compact Unicode)

其對(duì)應(yīng)PyCompactUnicodeObject結(jié)構(gòu)體,緊湊型Unicode以PyASCIIObject為基類烦却,非ASCII字符串可以通過(guò)PyUnicode_New函數(shù)為PyCompactUnicodeObject分配內(nèi)存并設(shè)置state.compact=1

typedef struct {
    PyASCIIObject _base;
    Py_ssize_t utf8_length;     /* utf8中的字節(jié)數(shù),不包括結(jié)尾的\0. */
    char *utf8;                 /* UTF-8表示形式(\0終止) */
    Py_ssize_t wstr_length;     /* wstr中的碼位個(gè)數(shù) */
} PyCompactUnicodeObject;

傳統(tǒng)的字符串(Legacy String)

其對(duì)應(yīng)PyUnicodeObject結(jié)構(gòu)體先巴,傳統(tǒng)的字符串對(duì)象會(huì)其中會(huì)包含兩種特殊狀態(tài)not ready和ready其爵。

傳統(tǒng)的字符串可以通過(guò)PyUnicode_FromUnicode為分配PyUnicodeObject結(jié)構(gòu)體分配內(nèi)存并封裝C級(jí)別的unicode字符串冒冬。 實(shí)際的字符串?dāng)?shù)據(jù)最初位于wstr塊中,并使用_PyUnicode_Ready函數(shù)復(fù)制到data的塊中摩渺。

typedef struct {
    PyCompactUnicodeObject _base;
    union {
        void *any;
        Py_UCS1 *latin1;
        Py_UCS2 *ucs2;
        Py_UCS4 *ucs4;
    } data;      /* 最小形式的Unicode緩沖區(qū) */
} PyUnicodeObject;

Unicode對(duì)象的原始基類除了PyObject外简烤,是以PyASCIIObject繼承而來(lái)的,PyCompactUnicodeObject類繼承PyASCIIObject,PyUnicodeObject繼承自PyCompactUnicodeObject摇幻,那么整個(gè)CPython3.3+的字符串體系可以用如下圖表示

Unicode字符串的字節(jié)寬度

在了解字符串如何創(chuàng)建有一個(gè)非常關(guān)鍵概念横侦,我們查看Include/cpython/unicodeobject.h源文件時(shí),CPython內(nèi)部定義了一個(gè)叫PyUnicode_Kind的枚舉類型,PyUnicode_New函數(shù)在實(shí)例化一個(gè)字符串對(duì)象時(shí)绰姻,會(huì)使用PyUnicode_Kind的枚舉值設(shè)定字符串對(duì)象內(nèi)部類state.kind的值枉侧,該字段將告知CPython的其他內(nèi)部代碼如何解讀C底層的char指針指向的字符串?dāng)?shù)據(jù)

enum PyUnicode_Kind {
/* String contains only wstr byte characters.  This is only possible
   when the string was created with a legacy API and _PyUnicode_Ready()
   has not been called yet.  */
    PyUnicode_WCHAR_KIND = 0,
/* Return values of the PyUnicode_KIND() macro: */
    PyUnicode_1BYTE_KIND = 1,
    PyUnicode_2BYTE_KIND = 2,
    PyUnicode_4BYTE_KIND = 4
};

字符串對(duì)象的內(nèi)存分配

前文說(shuō)到PyASCIIObject對(duì)象和PyCompactUnicodeObject對(duì)象都可以通過(guò)PyUnicode_New函數(shù)來(lái)創(chuàng)建狂芋,那么該函數(shù)如何區(qū)分它創(chuàng)建的目標(biāo)是PyASCIIObject榨馁,還是PyCompactUnicodeObject呢?盡管兩者是"父子"的繼承關(guān)系银酗,畢竟它們是不同的數(shù)據(jù)類型,仔細(xì)看一下實(shí)現(xiàn)代碼徒像,大體上PyUnicode_New函數(shù)是根據(jù)maxchar來(lái)區(qū)分創(chuàng)建什么字符串對(duì)象的黍特。

  • maxchar小于128,并且字符位寬為1個(gè)字節(jié)锯蛀,即標(biāo)準(zhǔn)的ASCII可識(shí)別的有效字符僅有128個(gè)灭衷,于是創(chuàng)建PyASCIIObject對(duì)象

  • maxchar小于256,并且字符位寬為1個(gè)字節(jié)旁涤,PyUnicode_New就創(chuàng)建PyCompactUnicodeObject對(duì)象翔曲。對(duì)于256個(gè)字符碼位組成的字符集,稱為擴(kuò)展的ASCII字符集(Extended ASCII Charset)

字節(jié)通常用于保存文本文檔中的各個(gè)字符。 在ASCII字符集中劈愚,每個(gè)0到127之間的二進(jìn)制值都被賦予一個(gè)特定字符瞳遍。 大多數(shù)計(jì)算機(jī)擴(kuò)展了ASCII字符集,以使用一個(gè)字節(jié)中可用的256個(gè)字符的整個(gè)范圍菌羽。 前128個(gè)字符處理特殊內(nèi)容掠械,例如常見(jiàn)外語(yǔ)中的重音字符。

  • maxchar小于65536注祖,并且字符位寬為2個(gè)字節(jié),PyUnicode_New就創(chuàng)建PyCompactUnicodeObject對(duì)象猾蒂,這種情況PyCompactUnicodeObject對(duì)象實(shí)際保存的是utf-16編碼的字符串。

  • 最后一種情況就是處理碼位個(gè)數(shù)大于65536且小于MAX_UNICODE是晨,通常此類的字符串的編碼是utf-32

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;

    /*返回空字符串的PyObject包裝類 */
    if (size == 0 && unicode_empty != NULL) {
        Py_INCREF(unicode_empty);
        return unicode_empty;
    }
    //處理ASCII字符集
    is_ascii = 0;
    is_sharing = 0;
    struct_size = sizeof(PyCompactUnicodeObject);
    if (maxchar < 128) {
        kind = PyUnicode_1BYTE_KIND;
        char_size = 1;
        is_ascii = 1;
        struct_size = sizeof(PyASCIIObject);
    }
    //處理ASCII擴(kuò)展的字符集
    else if (maxchar < 256) {
        kind = PyUnicode_1BYTE_KIND;
        char_size = 1;
    }
    //處理utf-16編碼的字符集
    else if (maxchar < 65536) {
        kind = PyUnicode_2BYTE_KIND;
        char_size = 2;
        if (sizeof(wchar_t) == 2)
            is_sharing = 1;
    }
    //處理utf-32編碼的字符串
    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;
    }

    /* Ensure we won't overflow the size. */
    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();

    /*
    來(lái)自_PyObject_New()的重復(fù)分配代碼肚菠,而不是對(duì)PyObject_New()的調(diào)用,
    因此我們能夠?yàn)閷?duì)象及其數(shù)據(jù)緩沖區(qū)分配空間罩缴。
     */
    obj = (PyObject *) PyObject_MALLOC(struct_size + (size + 1) * char_size);
    if (obj == NULL)
        return PyErr_NoMemory();
    //綁定PyUnicode_Type的類型信息
    obj = PyObject_INIT(obj, &PyUnicode_Type);
    if (obj == NULL)
        return NULL;

    unicode = (PyCompactUnicodeObject *)obj;
    if (is_ascii)
        //obj指針移動(dòng)
        data = ((PyASCIIObject*)obj) + 1;
    else
        data = unicode + 1;
    
    //設(shè)定state內(nèi)部類的狀態(tài)信息
    _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;
    if (is_ascii) {
        //NULL結(jié)束符
        ((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;
}

PyUnicode_New函數(shù)在計(jì)算要為字符串對(duì)象分配的內(nèi)存后蚊逢,即執(zhí)行下面這條語(yǔ)句后

obj = (PyObject *) PyObject_MALLOC(struct_size + (size + 1) * char_size);

那么PyASCIIObject的內(nèi)存分配如下圖


跟著會(huì)調(diào)用PyObject_INIT(obj, &PyUnicode_Type)函數(shù)來(lái)將PyUnicode_Type實(shí)例綁定到字符串對(duì)象的頭部层扶。
OK!我們之前談?wù)揚(yáng)yType_Type實(shí)例和各內(nèi)置數(shù)據(jù)類型的關(guān)系后时捌,你應(yīng)該清楚字符串對(duì)象的初始化匹配對(duì)應(yīng)的PyUnicode_Type實(shí)例,我們關(guān)注的是tp_new字段的函數(shù)指針unicode_new

PyTypeObject PyUnicode_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "str",                        /* tp_name */
    sizeof(PyUnicodeObject),      /* tp_basicsize */
    0,                            /* tp_itemsize */
    /* Slots */
    (destructor)unicode_dealloc,  /* tp_dealloc */
    .....
    unicode_repr,                 /* tp_repr */
    &unicode_as_number,           /* tp_as_number */
    &unicode_as_sequence,         /* tp_as_sequence */
    &unicode_as_mapping,          /* tp_as_mapping */
    (hashfunc) unicode_hash,      /* tp_hash*/
    ....
    (reprfunc) unicode_str,       /* tp_str */
    PyObject_GenericGetAttr,      /* tp_getattro */
    ....
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
    Py_TPFLAGS_UNICODE_SUBCLASS,   /* tp_flags */
    unicode_doc,                  /* tp_doc */
    .....
    PyUnicode_RichCompare,        /* tp_richcompare */
    0,                            /* tp_weaklistoffset */
    unicode_iter,                 /* tp_iter */
    0,                            /* tp_iternext */
    unicode_methods,              /* tp_methods */
    ....
    &PyBaseObject_Type,           /* tp_base */
    ....
    unicode_new,                  /* tp_new */
    PyObject_Del,                 /* tp_free */
};

若我們?yōu)橐韵伦址?分配內(nèi)存,對(duì)于CPython來(lái)說(shuō)怒医,它們默認(rèn)執(zhí)行utf-8執(zhí)行解碼也即29個(gè)字節(jié)

"我是一個(gè)自由開(kāi)發(fā)者!!"

當(dāng)整個(gè)PyUnicode_New函數(shù)返回時(shí),它構(gòu)建的PyASCIIObject如下內(nèi)存圖所示

字符串對(duì)象的初始化

一個(gè)簡(jiǎn)單的例子奢讨,有想過(guò)在一個(gè)Python腳本中稚叹,一個(gè)字符串字面量如何在CPython內(nèi)部完成字符串對(duì)象的實(shí)例化嗎?對(duì)于CPython3.9來(lái)說(shuō)拿诸,在實(shí)例化一個(gè)腳本內(nèi)固有的字符串(即單引號(hào)或雙引號(hào)內(nèi))扒袖,其實(shí)質(zhì)上從C級(jí)別的字符指針(const char*)指向的字符串字面量拷貝到PyUnicode_New函數(shù)分配的堆內(nèi)存的過(guò)程。而字符串初始化的函數(shù)調(diào)用起點(diǎn)為PyUnicode_DecodeUTF8Stateful函數(shù)亩码。

該流程省略了很多unicode字節(jié)碼解碼等特殊情況而得到一個(gè)簡(jiǎn)化的流程圖季率。經(jīng)過(guò)測(cè)試,幾乎所有Python腳本內(nèi)部所有字符串初始化的常規(guī)函數(shù)調(diào)用流程描沟。

有人可能會(huì)問(wèn)飒泻,你這個(gè)圖依據(jù)是怎么來(lái)的?我們已經(jīng)知道PyUnicode_New函數(shù)是一個(gè)為字符串對(duì)象間接分配內(nèi)存的函數(shù)接口吏廉,我們只要通過(guò)IDE工具查找并篩選引用該函數(shù)的上一個(gè)函數(shù)的結(jié)果泞遗,從中找到可能的函數(shù)調(diào)用路徑,并在各個(gè)可能的函數(shù)中插入一些printf函數(shù)席覆,打印函數(shù)名稱和相關(guān)傳入的關(guān)鍵參數(shù)史辙,就能推斷出該字符串對(duì)象初始化的軌跡了。還有慎用Python的Debug模型佩伤,因?yàn)槟銖腎DE工具看到內(nèi)存狀態(tài)可能和運(yùn)行時(shí)有所差異的聊倔。這個(gè)我在其他篇章也提到過(guò)。例如生巡,我們?cè)谝粋€(gè)測(cè)試的test.py文件中耙蔑,測(cè)試下面的Python字符串的實(shí)例化過(guò)程

"我是一個(gè)自由開(kāi)發(fā)者!!"

那么執(zhí)行python腳本將所有打印的運(yùn)行時(shí)信息重定向到一個(gè)文本中

./python test.py >debug.txt

如下圖所示,我們發(fā)現(xiàn)只要python的運(yùn)行時(shí)系統(tǒng)不論調(diào)用模塊間的內(nèi)置函數(shù)孤荣,還是用戶的自定義函數(shù)纵潦,只要涉及Python字符串對(duì)象都依次遵循上面PyASCIIObject/PyUnicodeObject初始化的函數(shù)調(diào)用過(guò)程

unicode_decode_utf8函數(shù)

回歸正題,我們先看一下一個(gè)關(guān)鍵的函數(shù)unicode_decode_utf8,該函數(shù)的完整代碼見(jiàn)Objects/unicodeobject.c的第4979行-5122行垃环,由于篇幅所限我這里將該函數(shù)拆解三個(gè)部分來(lái)討論邀层,先查看第4979行第5088行.該函數(shù)第一個(gè)參數(shù)是const char*類型字符指針s,這里重點(diǎn)討論該函數(shù)和它調(diào)用的ascii_decode函數(shù)的一些細(xì)節(jié)問(wèn)題遂庄。

static PyObject *
unicode_decode_utf8(const char *s, Py_ssize_t size,
                    _Py_error_handler error_handler, const char *errors,
                    Py_ssize_t *consumed)
{
    //處理空字符對(duì)象返回
    if (size == 0) {
        if (consumed)
            *consumed = 0;
        _Py_RETURN_UNICODE_EMPTY();
    }

    /* 處理僅為一個(gè)字符的情況寥院,且假定是ASCII字符 */
    if (size == 1 && (unsigned char)s[0] < 128) {
        if (consumed)
            *consumed = 1;
        return get_latin1_char((unsigned char)s[0]);
    }

    const char *starts = s;
    const char *end = s + size;

    //假定參數(shù)s是一堆由ASCII碼位組成的字符串
    PyObject *u = PyUnicode_New(size, 127);
    if (u == NULL) {
        return NULL;
    }
    s += ascii_decode(s, end, PyUnicode_1BYTE_DATA(u));
    if (s == end) {
        return u;
    }
    ....
}

unicode_decode_utf8函數(shù)假定傳入的C級(jí)別的字符串分三種情況實(shí)例化字符串對(duì)象

第1種情況:僅包含一個(gè)字符且位于標(biāo)準(zhǔn)的ASCII字符集區(qū)間內(nèi)

此時(shí)調(diào)用get_latin1_char函數(shù)并返回,那么get_latin1_char函數(shù)主要做的事情就是在整個(gè)Python解釋器運(yùn)行期間的緩存所有使用過(guò)的單個(gè)ASCII字符對(duì)象到一個(gè)長(zhǎng)度為256的unicode_latin1靜態(tài)數(shù)組中涛目。否則會(huì)為該字符調(diào)用PyUnicode_New函數(shù)分配內(nèi)存并緩存到unicode_latin1數(shù)組后再返回秸谢。

static PyObject*
get_latin1_char(unsigned char ch)
{
    PyObject *unicode;

#ifdef LATIN1_SINGLETONS
    unicode = unicode_latin1[ch];
    //如果該字符已緩存在unicode_latin1中凛澎,立即返回
    if (unicode) {
        Py_INCREF(unicode);
        return unicode;
    }
#endif
    //否則會(huì)為該字符分配內(nèi)存
    unicode = PyUnicode_New(1, ch);
    if (!unicode) {
        return NULL;
    }

    PyUnicode_1BYTE_DATA(unicode)[0] = ch;
    assert(_PyUnicode_CheckConsistency(unicode, 1));

#ifdef LATIN1_SINGLETONS
    Py_INCREF(unicode);
    unicode_latin1[ch] = unicode;
#endif
    return unicode;
}

第2種情況:假定字符串長(zhǎng)度不超過(guò)127,即由ASCII區(qū)間內(nèi)的任意編碼組成的字符串

這一邏輯推定的事實(shí)是前127個(gè)字符編碼(即ASCII字符集)是unicode字符集的一個(gè)子集。不論傳入的C級(jí)別字符串屬于哪一種情況估蹄,都需經(jīng)過(guò)一個(gè)特殊的ascii_decode函數(shù)塑煎,這個(gè)ascii_decode函數(shù)對(duì)于在如下情況通常給unicode_decode_utf8函數(shù)返回0的偏移量

  • 純ASCII字符串或純中文字符的unicode字符串
  • 任意ASCII字符和多國(guó)unicode字符編碼混合的字符串

PS:具體的源代碼請(qǐng)查看下面代碼,關(guān)于該函數(shù)CPython源代碼文檔,以及官方網(wǎng)站的API說(shuō)明都沒(méi)有提及臭蚁,因此最铁,我對(duì)其算法甚少理解,有大伙提供詳細(xì)信息垮兑,煩請(qǐng)跟帖評(píng)論留言冷尉。

static Py_ssize_t
ascii_decode(const char *start, const char *end, Py_UCS1 *dest)
{
    const char *p = start;
    const char *aligned_end = (const char *) _Py_ALIGN_DOWN(end, SIZEOF_LONG);

#if !defined(__m68k__)
#if SIZEOF_LONG <= SIZEOF_VOID_P
    //斷言dest是按8字節(jié)對(duì)齊
    assert(_Py_IS_ALIGNED(dest, SIZEOF_LONG));
    if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) {
        /* Fast path, see in STRINGLIB(utf8_decode) for
           an explanation. */
        /* Help allocation */
        const char *_p = p;
        Py_UCS1 * q = dest;
        while (_p < aligned_end) {
            unsigned long value = *(const unsigned long *) _p;
            if (value & ASCII_CHAR_MASK)
                break;
            *((unsigned long *)q) = value;
            _p += SIZEOF_LONG;
            q += SIZEOF_LONG;
        }
        p = _p;
        while (p < end) {
            if ((unsigned char)*p & 0x80)
                break;
            *q++ = *p++;
        }
        return p - start;
    }
#endif
#endif
    while (p < end) {
        /* Fast path, see in STRINGLIB(utf8_decode) in stringlib/codecs.h
           for an explanation. */
        if (_Py_IS_ALIGNED(p, SIZEOF_LONG)) {
            /* Help allocation */
            const char *_p = p;
            while (_p < aligned_end) {
                unsigned long value = *(const unsigned long *) _p;
                if (value & ASCII_CHAR_MASK)
                    break;
                _p += SIZEOF_LONG;
            }
            p = _p;
            if (_p == end)
                break;
        }
        if ((unsigned char)*p & 0x80)
            break;
        ++p;
    }
    memcpy(dest, start, p - start);
    return p - start;
}

我們上面示例字符串在初始化時(shí)過(guò)程前,我們?cè)谄銫函數(shù)內(nèi)用pinrtf函數(shù)的關(guān)鍵信息的輸出系枪,編譯后運(yùn)行如下圖

我們將上面的信息繪制成一個(gè)內(nèi)存圖雀哨,自然就一目了然啦。由于ascii_decode在函數(shù)返回后,對(duì)于任意的ASCII字符串對(duì)象或純Unicode編碼的字符串對(duì)象私爷,p-start的偏移量始終為0.

ss8..png

還有更多的細(xì)節(jié)雾棺,我們說(shuō)本實(shí)例的字符串的長(zhǎng)度是29字節(jié),前27個(gè)字節(jié)是unicode編碼衬浑,而最后兩個(gè)字節(jié)是純粹ASCII字符捌浩。其實(shí)UTF-8的思想是使用不同長(zhǎng)度的字節(jié)序列對(duì)各種Unicode字符進(jìn)行編碼, 標(biāo)準(zhǔn)的ASCII字符嚎卫,即包括拉丁字母數(shù)字和標(biāo)點(diǎn)符號(hào)使用一個(gè)字節(jié)嘉栓、ASCII擴(kuò)展字符都以2字節(jié)的順序排列宏榕、 韓文拓诸,中文和日文表意文字使用3字節(jié)序列。

小結(jié)

我們本篇討論了字符串對(duì)象的內(nèi)存分配PyUnicode_New函數(shù)麻昼,以及提出了CPython3.3+的字符串初始化的函數(shù)調(diào)用路徑奠支,先討論了unicode_decode_utf8函數(shù)和ascii_decode函數(shù)的一些細(xì)節(jié)問(wèn)題。下一篇會(huì)討論剩下的unicode_decode_utf8代碼細(xì)節(jié)抚芦。

更新中.....

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末倍谜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子叉抡,更是在濱河造成了極大的恐慌尔崔,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件褥民,死亡現(xiàn)場(chǎng)離奇詭異季春,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)消返,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門载弄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耘拇,“玉大人,你說(shuō)我怎么就攤上這事宇攻”古眩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵逞刷,是天一觀的道長(zhǎng)嘉涌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)亲桥,這世上最難降的妖魔是什么洛心? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮题篷,結(jié)果婚禮上词身,老公的妹妹穿的比我還像新娘。我一直安慰自己番枚,他們只是感情好法严,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著葫笼,像睡著了一般深啤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上路星,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天溯街,我揣著相機(jī)與錄音,去河邊找鬼洋丐。 笑死呈昔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的友绝。 我是一名探鬼主播堤尾,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼迁客!你這毒婦竟也來(lái)了郭宝?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤掷漱,失蹤者是張志新(化名)和其女友劉穎粘室,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體卜范,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡衔统,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缰冤。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡犬缨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出棉浸,到底是詐尸還是另有隱情怀薛,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布迷郑,位于F島的核電站枝恋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嗡害。R本人自食惡果不足惜焚碌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望霸妹。 院中可真熱鬧十电,春花似錦、人聲如沸叹螟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)罢绽。三九已至畏线,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間良价,已是汗流浹背寝殴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留明垢,地道東北人蚣常。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像袖外,于是被迫代替她去往敵國(guó)和親史隆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子魂务,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355