4. Python3源码—字符串(bytes)对象

简介: # 4.1. 字符串对象 字符串对象是“变长对象”。 ## 4.1.1. Python中的创建 Python中字符串(bytes)对象创建最重要的方法为PyBytes_FromStringAndSize,如下Python语句最终会调用到PyBytes_FromStringAndSize: ``` python a = b'hello' b = str(b'world') ``

4.1. 字符串对象

字符串对象是“变长对象”。

4.1.1. Python中的创建

Python中字符串(bytes)对象创建最重要的方法为PyBytes_FromStringAndSize,如下Python语句最终会调用到PyBytes_FromStringAndSize:

a = b'hello'
b = str(b'world')

4.1.2. PyBytes_FromStringAndSize的C调用栈

词法解析,最终调到PyBytes_FromStringAndSize,调用顺序如下:

// ast.c
ast_for_expr
=>ast_for_power
=>ast_for_atom_expr
=>ast_for_atom (case STRING)
=>parsestrplus
=>parsestr

// bytesobject.c
=>PyBytes_FromStringAndSize

4.1.3. PyBytes_FromStringAndSize源码

// bytesobject.c
PyObject *
PyBytes_FromStringAndSize(const char *str, Py_ssize_t size)
{
    PyBytesObject *op;
    if (size < 0) {
        PyErr_SetString(PyExc_SystemError,
            "Negative size passed to PyBytes_FromStringAndSize");
        return NULL;
    }
    if (size == 1 && str != NULL &&
        (op = characters[*str & UCHAR_MAX]) != NULL)
    {
#ifdef COUNT_ALLOCS
        one_strings++;
#endif
        Py_INCREF(op);
        return (PyObject *)op;
    }

    op = (PyBytesObject *)_PyBytes_FromSize(size, 0);
    if (op == NULL)
        return NULL;
    if (str == NULL)
        return (PyObject *) op;

    memcpy(op->ob_sval, str, size);
    /* share short strings */
    if (size == 1) {
        characters[*str & UCHAR_MAX] = op;
        Py_INCREF(op);
    }
    return (PyObject *) op;
}
// bytesobject.c
static PyObject *
_PyBytes_FromSize(Py_ssize_t size, int use_calloc)
{
    PyBytesObject *op;
    assert(size >= 0);

    if (size == 0 && (op = nullstring) != NULL) {
#ifdef COUNT_ALLOCS
        null_strings++;
#endif
        Py_INCREF(op);
        return (PyObject *)op;
    }

    if ((size_t)size > (size_t)PY_SSIZE_T_MAX - PyBytesObject_SIZE) {
        PyErr_SetString(PyExc_OverflowError,
                        "byte string is too large");
        return NULL;
    }

    /* Inline PyObject_NewVar */
    if (use_calloc)
        op = (PyBytesObject *)PyObject_Calloc(1, PyBytesObject_SIZE + size);
    else
        op = (PyBytesObject *)PyObject_Malloc(PyBytesObject_SIZE + size);
    if (op == NULL)
        return PyErr_NoMemory();
    (void)PyObject_INIT_VAR(op, &PyBytes_Type, size);
    op->ob_shash = -1;
    if (!use_calloc)
        op->ob_sval[size] = '\0';
    /* empty byte string singleton */
    if (size == 0) {
        nullstring = op;
        Py_INCREF(op);
    }
    return (PyObject *) op;
}

可以看到:

  • 字符串对象的C数据结构:
// bytesobject.h
typedef struct {
    PyObject_VAR_HEAD
    Py_hash_t ob_shash;
    char ob_sval[1];
} PyBytesObject;
  • 空串缓存:空串(nullstring)为同一个地址,第二次需要空串时,只是将计数加1,在_PyBytes_FromSize中实现空串缓存;
  • 字符缓冲池:字符(characters)为同一个地址,第二次需要该字符时,只是将计数加1,在PyBytes_FromStringAndSize中实现字符缓存;

4.2. 字符串对象的特性

支持tp_as_number、tp_as_sequence、tp_as_mapping这三种操作。

4.2.1. 数值操作

// bytesobject.c
&bytes_as_number,                           /* tp_as_number */

4.2.2. 序列操作

// bytesobject.c
&bytes_as_sequence,                       /* tp_as_sequence */
// bytesobject.c
static PySequenceMethods bytes_as_sequence = {
    (lenfunc)bytes_length, /*sq_length*/
    (binaryfunc)bytes_concat, /*sq_concat*/
    (ssizeargfunc)bytes_repeat, /*sq_repeat*/
    (ssizeargfunc)bytes_item, /*sq_item*/
    0,                  /*sq_slice*/
    0,                  /*sq_ass_item*/
    0,                  /*sq_ass_slice*/
    (objobjproc)bytes_contains /*sq_contains*/
};

因为没有实现PySequenceMethods中的设置方法,所以字符串不可变。

其中:

  • bytes_length
len(b'hello')
  • bytes_concat
b'hello' + b'world'

多个字符串相加效率低于join,join只分配一次内存;

  • bytes_repeat
b'hello'*10

效率要高于同个字符串相加;

  • bytes_item:暂时没有找到相应Python语句;
  • bytes_contains
b'h' in b'hello'

4.2.3. 关联操作

// bytesobject.c
&bytes_as_mapping,                         /* tp_as_mapping */
// bytesobject.c
static PyMappingMethods bytes_as_mapping = {
    (lenfunc)bytes_length,
    (binaryfunc)bytes_subscript,
    0,
};

其中:

  • bytes_subscript
test = b'hello world'
test[1]
test[0:5]

test[1]会走bytes_subscript方法的index分支,test[0:5]会走slice分支;

4.2.4. to string

// bytesobject.c
(reprfunc)bytes_repr,                       /* tp_repr */
bytes_str,                                  /* tp_str */

4.2.5. hash

// bytesobject.c
(hashfunc)bytes_hash,                       /* tp_hash */

4.2.6. 比较

// bytesobject.c
(richcmpfunc)bytes_richcompare,           /* tp_richcompare */

4.2.7. 内置方法

// bytesobject.c
bytes_methods,                              /* tp_methods */

4.3 参考

  • Python源码剖析
目录
相关文章
|
5天前
|
存储 Go 索引
牢记python对象的操作方式
【6月更文挑战第20天】在Python中,`hash()`和`is`帮助确定对象的相等性。`dir()`和`vars()`揭示对象的属性和内部表示,`__slots__`优化内存使用。列表和字典结构有不同的内存和性能特性,字典使用哈希表进行快速访问。
35 5
牢记python对象的操作方式
|
1天前
|
数据采集 开发者 Python
在Python中判断字符串中是否包含字母
在Python中判断字符串中是否包含字母
12 4
|
7天前
|
Shell Python
python中模块对象__file__
【6月更文挑战第12天】
17 8
|
7天前
|
Python
python中模块对象__name__
【6月更文挑战第12天】
19 7
|
6天前
|
Python
Python中的模块对象__package__
【6月更文挑战第13天】
14 5
|
5天前
|
算法 Java 程序员
Python内存管理用引用计数(对象的`ob_refcnt`)跟踪对象,但循环引用(如A-&gt;B-&gt;A)可导致内存泄漏。
【6月更文挑战第20天】Python内存管理用引用计数(对象的`ob_refcnt`)跟踪对象,但循环引用(如A-&gt;B-&gt;A)可导致内存泄漏。为解决此问题,Python使用`gc`模块检测并清理循环引用,可通过`gc.collect()`手动回收。此外,Python结合标记清除和分代回收策略,针对不同生命周期的对象优化垃圾回收效率,确保内存有效释放。
12 3
|
6天前
|
存储 数据安全/隐私保护 计算机视觉
Python教程:一文了解从Bytes到Bits的数据转换
在Python编程中,处理数据时经常需要在字节(bytes)和位(bits)之间进行转换。这种转换在网络通信、数据加密、图像处理等领域尤为常见。本文将详细介绍如何在Python中进行字节与位之间的转换,并提供一个实用的功能:如何在指定的位位置替换位数据。
18 4
|
6天前
|
存储 Python
Python中的模块对象__dict__
【6月更文挑战第13天】
13 4
|
7天前
|
存储 程序员 Python
python中模块对象__doc__
【6月更文挑战第12天】
11 5
|
7天前
|
Python
NumPy 是 Python 中的一个重要的科学计算包,其核心是一个强大的 N 维数组对象 Ndarray
【6月更文挑战第18天】NumPy的Ndarray是科学计算的核心,具有ndim(维度数)、shape(各维度大小)、size(元素总数)和dtype(数据类型)属性。方法包括T(转置)、ravel()(扁平化)、reshape()(改变形状)、astype()(转换数据类型)、sum()(求和)及mean()(计算平均值)。更多属性和方法如min/max等可在官方文档中探索。
26 5