08月12, 2020

8. String Object 的结构

上一节我们大体认识了 int 对象,在 Python 3 中实际上是以 LongObject 实现的,在 LongObject 的类型定义中,我们根据其类型名称 “int” 确认了这一点。回顾 int 对象的结构:

title

与 Python 2 不同,Python 3 中实现了任意长度的整数,整数的实际数据是紧跟在 PyVarObjectHead 之后的一个连续内存,从整数对象被创建开始,直到香消玉殒的那一刻,整数的 “值” 与这个整数对象都不会分离,它们是一个整体,无法在不进行内存分配的前提下任意修改整数的值。所以说整数对象是不可变对象,一旦创建就不能再被修改。回顾使用 Python 时,在 dict 类型中要求 key 必须为可 hash 的不可变类型,int 类型便在其中,int 类型不可变的结论符合以往的使用经验。

int 对象还有丰富的内容有待我们探索,不过我暂时不想在具体的细节上消耗太多时间,我希望尽快对 Python 3 有一个宏观肌理的印象,就继续看一下 Python 3 中另一个常见的类型 string 的实现。

String Object 的结构

Python 3 中 Stirng 对象与之前的实现变化比较大,源码中已经没有 stringobject. 的文件,已经被替换为 unicodeobject. 文件,可以在 Objects 和 Includes 目录下找到相关文件。根据 LongObject 的经验,我们首先找到 PyUnicode_Type 的定义:

//Objects/unicodeobject.c:15494
PyTypeObject PyUnicode_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "str",                        /* tp_name */
    sizeof(PyUnicodeObject),      /* tp_basicsize */
    0,                            /* tp_itemsize */
    /*
    *************省略了一些代码*************
    */
    unicode_new,                  /* tp_new */
    PyObject_Del,                 /* tp_free */
};

可以确认 PyUnicode_Type 正是我们要找到 String 类型。

前往 Include/cpython/unicodeobject.h 可以看到 PyUnicodeObject 的具体定义,删除其中的注释:

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;

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;

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

删掉注释代码量并不多,Python 3 深度支持 Unicode,对比 Python 2,String Object 做了比较大的调整, PyUnicodeObject 通过多层结构支持 ascii 和 unicode,一图胜千言,下面是 PyUnicodeObject 的结构。

title

实际上一个 PyUnicodeObject 对象是多种类型的复合体,可以表示最简单的 ascii 字符串、紧凑 unicode、unicode 3种字符串。 主要字段的意义:

  • length 保存了字符串种 codepoint 数量;
  • hash 保存字符串的哈希值,这里也表明字符串是一个不可变对象;
  • state 是一些状态标志位,只用了其中 8 位,剩余 24 位补齐,以便对齐到 32 位;
  • wstr 真正保存字符串的值,与 C 语言一样,以 '\0' 结束;

PyASCIIObject 构成了 PyUnicodeObject 的主体,注意到 unicode 对象有多个长度、数据字段,明天继续。

本文链接:http://www.thinkinpython.com/post/deep_python_vm_8.html

-- EOF --