- cpython/Objects/bytearrayobject.c
- cpython/Include/bytearrayobject.h
- cpython/Objects/clinic/bytearrayobject.c.h
The ob_alloc field represents the real allocated size in bytes
ob_bytes is the physical begin address, and ob_start is the logical begin address
ob_exports means how many other objects are sharing this buffer, like reference count in a way
>>> a = bytearray(b"")
>>> id(a)
4353755656
>>> b = bytearray(b"")
>>> id(b) # they are not shared
4353755712
After appending a character 'a', ob_alloc becomes 2, and ob_bytes and ob_start both point to the same address
a.append(ord('a'))The size growth pattern is shown in the code
/* Need growing, decide on a strategy */
if (size <= alloc * 1.125) {
/* Moderate upsize; overallocate similar to list_resize() */
alloc = size + (size >> 3) + (size < 9 ? 3 : 6);
}
else {
/* Major upsize; resize up to exact size */
alloc = size + 1;
}When appending, ob_alloc is 2 and the requested size is 2. Since 2 <= 2 * 1.125, the new allocated size is 2 + (2 >> 3) + 3 ==> 5
a.append(ord('b'))b = bytearray(b"abcdefghijk")After the slice operation, ob_start points to the real beginning of the content, and ob_bytes still points to the begin address of the malloced block
b[0:5] = [1,2]As long as the slice operation is going to shrink the bytearray and new_size < allocate / 2 is False, the resize operation won't shrink the actual malloced size
b[2:6] = [3, 4]Now, in the shrink operation, new_size < allocate / 2 is True, so the resize operation will be triggered
b[0:3] = [7,8]The growing pattern in slice operation is the same as the append operation
The requested size is 6. Since 6 < 6 * 1.125, the new allocated size is 6 + (6 >> 3) + 3 ==> 9
b[0:3] = [1,2,3,4]What does the field ob_exports mean? If you need details, you can refer to less-copies-in-python-with-the-buffer-protocol-and-memoryviews and PEP 3118
buf = bytearray(b"abcdefg")The bytearray implements the buffer protocol, and memoryview is able to access the internal data block via the buffer protocol. mybuf and buf both share the same internal block.
The field ob_exports becomes 1, which indicates how many objects are currently sharing the internal block via the buffer protocol
mybuf = memoryview(buf)
mybuf[1] = 3The same applies to the mybuf2 object (ob_exports doesn't change because you need to call the C function defined by the buf object via the buffer protocol; mybuf2 only calls the slice function of mybuf)
mybuf2 = mybuf[:4]
mybuf2[0] = 1ob_exports becomes 2
mybuf3 = memoryview(buf)ob_exports becomes 0
del mybuf
del mybuf2
del mybuf3












