由于 unicode 在 Python 中使用频率极高,Python 3 针对 unicode 提供了大量接口和优化,其中最基本的是 PyUnicode_New 和 _PyUnicode_New 函数,分别提供了两种不同的存储方式。
PyUnicode_New
删除掉一些与主要逻辑无关的代码,逻辑并不复杂。
- // Object/unicodeobject.c:1387
- 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; // is_sharing 决定了 wstr 指针是否共享数据区,
- // 这里假定 sizeof(w_char_t) 为 4。
- Py_ssize_t char_size;
- Py_ssize_t struct_size;
- is_ascii = 0;
- is_sharing = 0;
- // 省略一些代码,根据 maxchar 决定使用那种结构,确定结构体尺寸 struct_size
- // 从内存池分配内存
- obj = (PyObject *) PyObject_MALLOC(struct_size + (size + 1) * char_size);
- // 设置对象类型为 unicode type
- _PyObject_Init(obj, &PyUnicode_Type);
- // 注意,这里计算 data 指针的位置
- unicode = (PyCompactUnicodeObject *)obj;
- if (is_ascii)
- data = ((PyASCIIObject*)obj) + 1;
- else
- data = unicode + 1;
- // 省略一些代码,设置 unicode object 的长度、state 属性
- // 由于 Python 使用与 C 同样的策略,在字符串结尾需要设置 '\0' 作为 terminal code
- // 根据不同类型设置 data 结尾处的值。
- if (is_ascii) {
- ((char*)data)[size] = 0;
- _PyUnicode_WSTR(unicode) = NULL;
- }
- else if (kind == PyUnicode_1BYTE_KIND) {
- // ...
- }
- else {
- // 省略,根据不同的字符宽度计算 data 结尾的位置,并设置为 0
- // 如果字符宽度与 w_char_t 相同,那么设置 unicode object 的 wchar 指针指向数据区 data。
- if (is_sharing) {
- _PyUnicode_WSTR_LENGTH(unicode) = size;
- _PyUnicode_WSTR(unicode) = (wchar_t *)data;
- }
- // ...
- }
- return obj;
- }
c
通过 PyUnicode_New 创建的 unicode 对象是一个连续对象,即对象的头部与数据是一体的,一个 ASCII 字符串对象看起来是这样:
是一个 ASCIIObject 为头部的 C 风格字符串对象。 4 字节宽的字符串对象看起来是这样:
则是一个 PyCompactUnicodeObject 为头部的 C 风格字符串对象。当 w_char_t 大小与 unicode 字符宽度一致事,wstr 指针指向自身的数据段。
_PyUnicode_New
这个函数则更加底层,不是开放的 C API。
- static PyUnicodeObject *
- _PyUnicode_New(Py_ssize_t length)
- {
- PyUnicodeObject *unicode;
- size_t new_size;
- // 创建一个 PyUnicodeObject 类型的 PyObject 对象
- unicode = PyObject_New(PyUnicodeObject, &PyUnicode_Type);
- // 计算数据区域大小
- new_size = sizeof(Py_UNICODE) * ((size_t)length + 1);
- // 设置 unicode 对象的一些属性,除了 hash 设置为 -1, length设置为 length,其他设置为 0
- // 从内存池分配真正的数据区
- _PyUnicode_WSTR(unicode) = (Py_UNICODE*) PyObject_MALLOC(new_size);
- // 设置数据区域结尾和开头的 ‘\0’,逻辑上与 memset 数据区为 0 一样
- _PyUnicode_WSTR(unicode)[0] = 0;
- _PyUnicode_WSTR(unicode)[length] = 0;
- return unicode;
- }
c
通过这个函数创建 unicode 实例,只需要一个长度参数,非常简单。它创建的对象结构与前一种函数也不同,数据与类型对象通过指针关联起来。看起来像这样:
这是一个非常原始的数据结构,默认会使用 w_char_t 保存数据。使用该函数创建对象后还需要进一步处理才能使用。