字节的结构体 PyBytesObject
浮点数的结构体 PyBytesObject ,定义在头文件 Include/bytesobject.h 中,包含PyObject_VAR_HEAD 说明字节是可变大小的对象。
// Include/bytesobject.h
typedef struct {
PyObject_VAR_HEAD // 可变大小的对象
Py_hash_t ob_shash;
char ob_sval[1];
/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the string or -1 if not computed yet.
*/
} PyBytesObject;
// 展开 PyObject_VAR_HEAD
typedef struct {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt; // 引用计数 8字节
struct _typeobject *ob_type; // 类型 8字节
Py_ssize_t ob_size; // 对象包含的元素数量,即字节序列的长度
Py_hash_t ob_shash; // 哈希值,如果哈希值尚未计算,通常会将其设置为 -1
char ob_sval[1]; // 字节序列的实际数据,其长度为 1,但实际分配的内存可能大于 1,C语言中字节序列以 \0 字符结尾,计算ob_size的时候不包括\0
}
字节内存大小
把 PyBytesObject 展开后发现ob_refcnt 是整数型长度 8 字节,ob_type 是指针长度是 8 字节,ob_size 是整数型长度是 8 字节,ob_shash 是整数型长度是 8 字节,ob_sval 是 char 类型的数组长度是 n 1 字节 再加上末尾的 \0 的 1 字节,所以字节对象的大小是变长的 32+n1+1 字节,可以在 Python 中验证。
s = b"" # 空字节串长度为0 内存占用为 32 + 0*1 + 1 = 33
s.__sizeof__() # 33
s1 = b'0x1715f7b01e0' # 字节长度为 13,内存占用 32 + 13 * 1 + 1 = 46
s1.__sizeof__() # 46
# 另一种计算方式
import sys
sys.getsizeof(s1) # 46
字节的类型对象 PyBytes_Type
字节对象的类型的结构体是 PyBytes_Type,定义在头文件 Include/bytesobject.c 中。和其他类型对象一样,提供了多个函数来检查和操作字节对象,包括字节的创建bytes_new、字节的销毁bytes_dealloc、字节的函数集bytes_as_number 包含取余等计算、序列对象的方法集bytes_as_sequence、映射相关的方法集bytes_as_mapping、字节的哈希计算bytes_hash、用于生成字节字符串表示的函数bytes_repr 等等。
// Objects/bytesobject.c
PyTypeObject PyBytes_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"bytes",
PyBytesObject_SIZE, // tp_basicsize 基本大小
sizeof(char), // tp_itemsize可变长对象的每个元素的大小
bytes_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)bytes_repr, /* tp_repr */
&bytes_as_number, /* tp_as_number */
&bytes_as_sequence, /* tp_as_sequence */
&bytes_as_mapping, /* tp_as_mapping */
(hashfunc)bytes_hash, /* tp_hash */
0, /* tp_call */
bytes_str, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
&bytes_as_buffer, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_BYTES_SUBCLASS, /* tp_flags */
bytes_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
(richcmpfunc)bytes_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
bytes_iter, /* tp_iter */
0, /* tp_iternext */
bytes_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
&PyBaseObject_Type, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
bytes_new, /* tp_new */
PyObject_Del, /* tp_free */
};
但是和浮点数类型不一样的地方,tp_basicsize 对应的是PyBytesObject_SIZE,tp_itemsize 对应的是sizeof(char)
// Objects/bytesobject.c
// 用于计算 PyBytesObject 结构体的基本大小,不包括存储在 ob_sval 字段中的实际字节数据
#define PyBytesObject_SIZE (offsetof(PyBytesObject, ob_sval) + 1)
// ob_sval的类型是char的数组,所以数组中每个元素的长度为sizeof(char)
sizeof(char)
值为 0 的字段表示这些字段在初始化时没有被显式赋值或使用了默认值,即最终调用的时候使用的是 PyTypeObject 中对应的值。
- tp_print: 设为 0 表示没有定义打印函数,继承了PyTypeObject 中的打印函数。
- tp_getattr 和 tp_setattr: 设为 0 表示没有定义获取和设置属性的函数。
- tp_call: 设为 0 表示这个类型对象不能被调用(即 float 对象不是可调用的)。
- tp_init: 设为 0 表示继承了PyTypeObject 中的初始化函数。
字节创建
第一种和浮点数一样是通过类型对象创建,调用类型对象中的 tp_new 函数,字节对象对应的是 bytes_new,可以看到经过一些列的检测和判断,最终落在了 PyBytes_FromObject 函数上。
看下类型对象创建时 python 中的函数 bytes 调用和传参
"""
help(bytes)
class bytes(object)
| bytes(iterable_of_ints) -> bytes
| bytes(string, encoding[, errors]) -> bytes
| bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer
| bytes(int) -> bytes object of size given by the parameter initialized with null bytes
| bytes() -> empty bytes object
|
| Construct an immutable array of bytes from:
| - an iterable yielding integers in range(256)
| - a text string encoded using the specified encoding
| - any object implementing the buffer API.
| - an integer
"""
bytes([1,2,3,4]) # b'\x01\x02\x03\x04'
bytes("1234", encoding="gbk") # b'1234'
bytes(b"1234") # b'1234'
bytes(4) # b'\x00\x00\x00\x00' 生成长度为4的 null 字节
bytes() # b''
python 中b = bytes(b"1234")
通过类型对象创建字节对象。
// Objects/bytesobject.c
static PyObject *
bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *x = NULL;
const char *encoding = NULL;
const char *errors = NULL;
PyObject *new = NULL;
PyObject *func;
Py_ssize_t size;
static char *kwlist[] = {"source", "encoding", "errors", 0};
_Py_IDENTIFIER(__bytes__);
if (type != &PyBytes_Type)
return bytes_subtype_new(type, args, kwds);
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:bytes", kwlist, &x,
&encoding, &errors))
return NULL;
if (x == NULL) {
// 调用bytes()时会走到这
if (encoding != NULL || errors != NULL) {
PyErr_SetString(PyExc_TypeError,
"encoding or errors without sequence "
"argument");
return NULL;
}
return PyBytes_FromStringAndSize(NULL, 0);
}
if (encoding != NULL) {
// 调用 bytes("123", encoding="gbk") 会走到这
/* Encode via the codec registry */
if (!PyUnicode_Check(x)) {
PyErr_SetString(PyExc_TypeError,
"encoding without a string argument");
return NULL;
}
new = PyUnicode_AsEncodedString(x, encoding, errors);
if (new == NULL)
return NULL;
assert(PyBytes_Check(new));
return new;
}
if (errors != NULL) {
PyErr_SetString(PyExc_TypeError,
PyUnicode_Check(x) ?
"string argument without an encoding" :
"errors without a string argument");
return NULL;
}
/* We'd like to call PyObject_Bytes here, but we need to check for an
integer argument before deferring to PyBytes_FromObject, something
PyObject_Bytes doesn't do. */
func = _PyObject_LookupSpecial(x, &PyId___bytes__);
if (func != NULL) {
new = _PyObject_CallNoArg(func);
Py_DECREF(func);
if (new == NULL)
return NULL;
if (!PyBytes_Check(new)) {
PyErr_Format(PyExc_TypeError,
"__bytes__ returned non-bytes (type %.200s)",
Py_TYPE(new)->tp_name);
Py_DECREF(new);
return NULL;
}
return new;
}
else if (PyErr_Occurred())
return NULL;
if (PyUnicode_Check(x)) {
PyErr_SetString(PyExc_TypeError,
"string argument without an encoding");
return NULL;
}
/* Is it an integer? */
if (PyIndex_Check(x)) {
// 调用 bytes(3) 会走到这
size = PyNumber_AsSsize_t(x, PyExc_OverflowError);
if (size == -1 && PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(PyExc_TypeError))
return NULL;
PyErr_Clear(); /* fall through */
}
else {
if (size < 0) {
PyErr_SetString(PyExc_ValueError, "negative count");
return NULL;
}
new = _PyBytes_FromSize(size, 1);
if (new == NULL)
return NULL;
return new;
}
}
// 调用 bytes(b"123") 会走到这
return PyBytes_FromObject(x);
}
// 用于将一个对象转换为字节对象
// 函数尝试通过调用对象的 __bytes__ 方法或其他合适的转换方式来生成字节对象
PyObject *
PyBytes_FromObject(PyObject *x)
{
PyObject *it, *result;
if (x == NULL) {
PyErr_BadInternalCall();
return NULL;
}
// 检查输入对象是否已经是字节对象
if (PyBytes_CheckExact(x)) {
Py_INCREF(x);
return x;
}
/* Use the modern buffer interface */
if (PyObject_CheckBuffer(x))
return _PyBytes_FromBuffer(x);
if (PyList_CheckExact(x))
return _PyBytes_FromList(x);
if (PyTuple_CheckExact(x))
return _PyBytes_FromTuple(x);
if (!PyUnicode_Check(x)) {
it = PyObject_GetIter(x);
if (it != NULL) {
result = _PyBytes_FromIterator(it, x);
Py_DECREF(it);
return result;
}
if (!PyErr_ExceptionMatches(PyExc_TypeError)) {
return NULL;
}
}
PyErr_Format(PyExc_TypeError,
"cannot convert '%.200s' object to bytes",
x->ob_type->tp_name);
return NULL;
}
第二种是通过 C API 进行创建,python 中b = b"1234"
通过 C API 创建字节对象。
// Objects/bytesobject.c
PyObject *
PyBytes_FromStringAndSize(const char *str, Py_ssize_t size)
{
PyBytesObject *op;
if (size < 0) {
// 如果 len 为负数,函数会设置 PyExc_SystemError 异常并返回 NULL
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;
}
// 使用 PyObject_Malloc 为 PyBytesObject 分配内存。
// 分配的大小是 sizeof(PyBytesObject) + len,以便存储字节对象本身和它的内容
op = (PyBytesObject *)_PyBytes_FromSize(size, 0);
if (op == NULL)
return NULL;
if (str == NULL)
return (PyObject *) op;
// 如果 v 非 NULL,使用 memcpy 将 v 指向的字符串的前 len 个字节复制到新分配的字节对象中
memcpy(op->ob_sval, str, size);
/* share short strings */
if (size == 1) {
characters[*str & UCHAR_MAX] = op;
Py_INCREF(op);
}
// 将新创建并初始化的 PyBytesObject 转换为 PyObject*,并返回给调用者
return (PyObject *) op;
}
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();
// 使用 PyObject_Init_VAR 初始化变量长度对象 (PyVarObject)
(void)PyObject_INIT_VAR(op, &PyBytes_Type, size);
op->ob_shash = -1;
// 无论 v 是否为 NULL,都将新字节对象的末尾设置为 '\0'
if (!use_calloc)
op->ob_sval[size] = '\0';
/* empty byte string singleton */
if (size == 0) {
nullstring = op;
Py_INCREF(op);
}
// 将新创建并初始化的 PyBytesObject 转换为 PyObject*,并返回给调用者
return (PyObject *) op;
}
字节对象的销毁
销毁对象是调用指针 tp_dealloc 所指的函数,字节对象对应的就是 bytes_dealloc,可以看到直接调用了tp_free(op)函数, tp_free内存释放函数指向 PyObject_Free,负责实际的内存释放。
// Objects/bytesobject.c
static void
bytes_dealloc(PyObject *op)
{
Py_TYPE(op)->tp_free(op);
}
字节的缓存机制
字节对象的缓存是一个PyBytesObject 类型的数组,存在characters 中,长度为 256。
// Objects/bytesobject.c
#define UCHAR_MAX 0xff
static PyBytesObject *characters[UCHAR_MAX + 1];
PyObject *
PyBytes_FromStringAndSize(const char *str, Py_ssize_t size)
{
PyBytesObject *op;
if (size < 0) {
// 如果 len 为负数,函数会设置 PyExc_SystemError 异常并返回 NULL
PyErr_SetString(PyExc_SystemError,
"Negative size passed to PyBytes_FromStringAndSize");
return NULL;
}
// 长度为1的字节,如果在缓存中,就直接获取并返回
if (size == 1 && str != NULL &&
(op = characters[*str & UCHAR_MAX]) != NULL)
{
#ifdef COUNT_ALLOCS
one_strings++;
#endif
Py_INCREF(op);
return (PyObject *)op;
}
// 使用 PyObject_Malloc 为 PyBytesObject 分配内存。
// 分配的大小是 sizeof(PyBytesObject) + len,以便存储字节对象本身和它的内容
op = (PyBytesObject *)_PyBytes_FromSize(size, 0);
if (op == NULL)
return NULL;
if (str == NULL)
return (PyObject *) op;
// 如果 v 非 NULL,使用 memcpy 将 v 指向的字符串的前 len 个字节复制到新分配的字节对象中
memcpy(op->ob_sval, str, size);
/* share short strings */
// 长度为1时,缓存起来
if (size == 1) {
characters[*str & UCHAR_MAX] = op;
Py_INCREF(op);
}
// 将新创建并初始化的 PyBytesObject 转换为 PyObject*,并返回给调用者
return (PyObject *) op;
}
缓存过程:
1、当字节长度为 1,并且不为空的时候,会缓存在characters 中
2、当初始化的时候,长度为 1 的字节,先会判断是否在缓存中,如果在缓存中就直接返回。
在 python 中验证 :
"""
两个单字节的地址一样,多字节的地址不同
"""
a = b'a'
id(a) # 2581807388640
z = b'a'
id(z) # 2581807388640
a1 = b"abc"
id(a1) # 1902678903424
z1 = b"abc"
id(z1) # 1902678904480
字节的属性和函数
PyBytes_Type 中还定义了一些字节对象相关的属性和函数等
bytes_hash 字节的哈希值
可以看到计算完哈希后,将哈希值也赋给了字节对象的ob_shash,因为字节对象的哈希是经常用到的,所以为了效率计算一次后,保存在对象中。
static Py_hash_t
bytes_hash(PyBytesObject *a)
{
if (a->ob_shash == -1) {
/* Can't fail */
a->ob_shash = _Py_HashBytes(a->ob_sval, Py_SIZE(a));
}
return a->ob_shash;
}
bytes_as_number 结构体是所有数值的函数集,但是只包含取余函数,但是真实的函数功能并不是取余,而是用来%格式化字节串。
static PyNumberMethods bytes_as_number = {
0, /*nb_add*/
0, /*nb_subtract*/
0, /*nb_multiply*/
bytes_mod, /*nb_remainder*/
};
static PyObject *
bytes_mod(PyObject *self, PyObject *arg)
{
if (!PyBytes_Check(self)) {
Py_RETURN_NOTIMPLEMENTED;
}
// 格式化字节串
return _PyBytes_FormatEx(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
arg, 0);
}
bytes_as_sequence 结构体是所有序列的函数集,包含长度、连接、重复、是否包含、索引等
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 是否包含 in */
};
bytes_length 获取对象长度
static Py_ssize_t
bytes_length(PyBytesObject *a)
{
return Py_SIZE(a);
}
bytes_concat 拼接两个字节串
static PyObject *
bytes_concat(PyObject *a, PyObject *b)
{
Py_buffer va, vb;
PyObject *result = NULL;
va.len = -1;
vb.len = -1;
if (PyObject_GetBuffer(a, &va, PyBUF_SIMPLE) != 0 ||
PyObject_GetBuffer(b, &vb, PyBUF_SIMPLE) != 0) {
PyErr_Format(PyExc_TypeError, "can't concat %.100s to %.100s",
Py_TYPE(b)->tp_name, Py_TYPE(a)->tp_name);
goto done;
}
/* Optimize end cases */
if (va.len == 0 && PyBytes_CheckExact(b)) {
result = b;
Py_INCREF(result);
goto done;
}
if (vb.len == 0 && PyBytes_CheckExact(a)) {
result = a;
Py_INCREF(result);
goto done;
}
if (va.len > PY_SSIZE_T_MAX - vb.len) {
PyErr_NoMemory();
goto done;
}
result = PyBytes_FromStringAndSize(NULL, va.len + vb.len);
if (result != NULL) {
memcpy(PyBytes_AS_STRING(result), va.buf, va.len);
memcpy(PyBytes_AS_STRING(result) + va.len, vb.buf, vb.len);
}
done:
if (va.len != -1)
PyBuffer_Release(&va);
if (vb.len != -1)
PyBuffer_Release(&vb);
return result;
}
字节相关的宏定义和函数
#define PyBytes_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_BYTES_SUBCLASS)
#define PyBytes_CheckExact(op) (Py_TYPE(op) == &PyBytes_Type)