Indexing, Slicing, and Immutability of Python Bytes

We have previously introduced many aspects of Python; today, let’s discuss the content of Python bytes, focusing on its indexing, slicing operations, and its core feature (immutability).Bytes, which are byte strings, are an ordered sequence where each byte has a unique position number, known as an index. You can directly access the value of a single byte using its index. Let’s look at a simple example:

# Define a byte string
v = b'ABCDEFGH'
# Forward indexing: 0~7 (8 bytes total)
print(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7])
# Output: 65 66 67 68 69 70 71 72 (corresponding to ASCII codes A~H)
# Backward indexing: -8~-1 (corresponding to forward indexing)
print(v[-8], v[-7], v[-6], v[-5], v[-4], v[-3], v[-2], v[-1])
# Output: 65 66 67 68 69 70 71 72

The above is a simple example. The indexing of bytes follows certain rules: first, the index type must be an integer, and you cannot specify it using an assignment like index=. The index range supports both forward and backward indexing, starting from 0 and -1, respectively. 0 represents the first byte, while -1 represents the last byte. If the index exceeds the actual range of the byte string, it will raise an IndexError: index out of range.Slicing of byte strings is somewhat similar to that of strings, mainly used to extract a continuous subsequence from the byte string. The syntax is [start:end:step], and the returned value is still of bytes type.

# 1. Extract all bytes (multiple equivalent forms)
print(v[:])      # b'ABCDEFGH'
print(v[::])     # b'ABCDEFGH'
print(v[None:None:None]) # b'ABCDEFGH'
print(v[0:8:1])  # b'ABCDEFGH'
print(v[-100:100:1]) # b'ABCDEFGH' (out of bounds does not raise an error)
# 2. Step of 2: take one byte every 1 byte (forward)
print(v[::2])    # b'ACEG' (taking indices 0, 2, 4, 6)
# 3. Step of -2: take one byte every 1 byte (backward)
print(v[::-2])   # b'HFDB' (taking indices 7, 5, 3, 1)
# 4. Fixed start, unspecified end: take from start to the end
print(v[2:])     # b'CDEFGH' (start=2)
print(v[-6:])    # b'CDEFGH' (negative start=-6, equivalent to positive 2)
# 5. Fixed end, unspecified start: take from the beginning to end (not inclusive)
print(v[:6])     # b'ABCDEF' (end=6)
print(v[:-2])    # b'ABCDEF' (negative end=-2, equivalent to positive 6)
# 6. Specify both start and end: take the middle segment
print(v[2:6])    # b'CDEF' (start=2, end=6)
print(v[-6:-2])  # b'CDEF' (negative start=-6, end=-2, equivalent to positive 2, 6)
# 7. Backward step + start/end: note the direction consistency, otherwise returns an empty byte string
print(v[2:6:-2]) # b'' (step is negative, start should be greater than end, here 2 < 6, no result)

The most core feature of byte strings is their immutability. Once created, the internal bytes cannot be modified through indexing or slicing, nor can they be deleted using the del statement. Let’s try to modify it:

v = b'abcdef'
# All the following operations will raise an error
v[1] = b'X'                # Index assignment
v[-5] = b'X'               # Backward index assignment
v[3:5] = [b'Y', b'Z']      # Slice assignment (list)
v[-3:-1] = b'YZ'           # Slice assignment (byte string)
v[1], v[3:5] = b'X', b'YZ' # Multiple variable assignment

This will raise a TypeError: ‘bytes’ object does not support item assignment.As for deletion, you can try it yourself.Byte strings are immutable, so multiple variable assignments will share the same byte string object (rather than creating a copy).

# Three variables point to the same byte string
v1 = v2 = v3 = b'ABCDE'
print(v1) # b'ABCDE'
print(v2) # b'ABCDE'
print(v3) # b'ABCDE'

Additionally, since byte strings are immutable, Python does not need to create copies of them. Whether using copy.copy(), copy.deepcopy(), or bytes(), the result is always a reference to the original byte string. We can use the is keyword to verify this.

import copy
v1 = b'ABCDE'
# The following four "copy" methods all point to the same object as v1
v2 = copy.copy(v1)
v2 = bytes(v1)
v2 = v1[:]
v2 = copy.deepcopy(v1)
# Verification: both point to the same object, and the internal bytes are also identical
print(v1 is v2, v1[2] is v2[2]) # Output: True True

Through the above content, I believe you have gained a further understanding of bytes. I hope this article is helpful to you. If you find it useful, please give it a thumbs up.

Leave a Comment