In the previous tutorials the immutable string class and byte class were examined. The byte class in particular is an immutable Sequence of byte values. Immutable means that once an instance is instantiated, it cannot be modified. The bytearray is a mutable Sequence of byte values which means that it can be modified after instantiation. Because the bytearray is a mutable version of the byte, it has consistent data model identifiers to the byte and most of these previously used identifiers can be considered as read only identifiers. The bytearray also has mutable identifiers which can be considered as write identifiers which update the bytearray in place.
Table of contents
Initialisation
Inputting bytearray() will display the docstring of the initialisation signature of the byte class as a popup balloon. Recall some IDEs such as JupyterLab may require the keypress shift ⇧ and tab ↹ to invoke the docstring:
Init signature: bytearray(self, /, *args, **kwargs) Docstring: bytearray(iterable_of_ints) -> bytearray bytearray(string, encoding[, errors]) -> bytearray bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer bytearray(int) -> bytes array of size given by the parameter initialized with null bytes bytearray() -> empty bytes array Construct a mutable bytearray object from: - an iterable yielding integers in range(256) - a text string encoded using the specified encoding - a bytes or a buffer object - any object implementing the buffer API. - an integer Type: type Subclasses:
The initialisation signature of the bytearray is consistent with that of the byte. For simplicity a bytearray will be constructed from an ASCII string and encoding of ASCII:
greeting_r = bytes('Γειά σου Κόσμε!', encoding='UTF-8')
greeting_rw = bytearray('Γειά σου Κόσμε!', encoding='UTF-8')
Identifiers
If the identifiers of greeting_r are examined, the following immutable functions are observed. These all return a value:
- capitalize – function
- center – function
- count – function
- decode – function
- endswith – function
- expandtabs – function
- find – function
- fromhex – function
- hex – function
- index – function
- isalnum – function
- isalpha – function
- isascii – function
- isdigit – function
- islower – function
- isspace – function
- istitle – function
- isupper – function
- join – function
- ljust – function
- lower – function
- lstrip – function
- maketrans – function
- partition – function
- removeprefix – function
- removesuffix – function
- replace – function
- rfind – function
- rindex – function
- rjust – function
- rpartition – function
- rsplit – function
- rstrip – function
- split – function
- splitlines – function
- startswith – function
- strip – function
- swapcase – function
- title – function
- translate – function
- upper – function
- zfill – function
If the identifiers of greeting_rw are examined, all the identifiers of greeting_r are seen in addition to the following immutable functions. Most of the mutable functions do not return a value and instead update the bytearray instance in place:
- append – mutable function
- clear – mutable function
- extend – mutable function
- insert – mutable function
- pop – mutable function
- remove – mutable function
The exception is pop which both returns a value and updates the bytearray in place.
If the identifiers of greeting_r are examined, the following are observed:
- __add__ – instance
- __bytes__ – function
- __class__ – class
- __contains__ – instance
- __delattr__ – instance
- __dir__ – function
- __doc__ – instance
- __eq__ – instance
- __format__ – function
- __ge__ – instance
- __getattribute__ – instance
- __getitem__ – instance
- __getnewargs__ – function
- __getstate__ – function
- __gt__ – instance
- __hash__ – instance
- __init__ – instance
- __init_subclass__ – function
- __iter__ – instance
- __le__ – instance
- __len__ – instance
- __lt__ – instance
- __mod__ – instance
- __mul__ – instance
- __ne__ – instance
- __new__ – function
- __reduce__ – function
- __reduce_ex__ – function
- __repr__ – instance
- __rmod__ – instance
- __rmul__ – instance
- __setattr__ – instance
- __sizeof__ – function
- __str__ – instance
- __subclasshook__ – function
If the data model identifiers of greeting_rw are examined, all the data model identifiers of greeting_r are seen in addition to the following immutable data model identifiers:
- __alloc__ – function
- __delitem__ – instance
- __setitem__ – instance
- __iadd__ – instance
- __imul__ – instance
A MutableSequence typically has __setitem__ and __delitem__ which complement __getitem__ from the (immutable) Sequence. A MutableSequence also has the append, reverse, extend, pop, remove identifiers. Finally it has the in place operators such as __iadd__ and __imul__.
Recall that details about each of the identifiers can be seen by using help on the bytearray class:
help(bytearray)
Help on class bytearray in module builtins:
class bytearray(object)
| bytearray(iterable_of_ints) -> bytearray
| bytearray(string, encoding[, errors]) -> bytearray
| bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer
| bytearray(int) -> bytes array of size given by the parameter initialized with null bytes
| bytearray() -> empty bytes array
|
| Construct a mutable bytearray object from:
| - an iterable yielding integers in range(256)
| - a text string encoded using the specified encoding
| - a bytes or a buffer object
| - any object implementing the buffer API.
| - an integer
|
| Methods defined here:
|
| __add__(self, value, /)
| Return self+value.
|
| __alloc__(...)
| B.__alloc__() -> int
|
| Return the number of bytes actually allocated.
|
| __contains__(self, key, /)
| Return key in self.
|
| __delitem__(self, key, /)
| Delete self[key].
|
| __eq__(self, value, /)
| Return self==value.
|
| __ge__(self, value, /)
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __getitem__(self, key, /)
| Return self[key].
|
| __gt__(self, value, /)
| Return self>value.
|
| __iadd__(self, value, /)
| Implement self+=value.
|
| __imul__(self, value, /)
| Implement self*=value.
|
| __init__(self, /, *args, **kwargs)
| Initialize self. See help(type(self)) for accurate signature.
|
| __iter__(self, /)
| Implement iter(self).
|
| __le__(self, value, /)
| Return self<=value.
|
| __len__(self, /)
| Return len(self).
|
| __lt__(self, value, /)
| Return self<value.
|
| __mod__(self, value, /)
| Return self%value.
|
| __mul__(self, value, /)
| Return self*value.
|
| __ne__(self, value, /)
| Return self!=value.
|
| __reduce__(self, /)
| Return state information for pickling.
|
| __reduce_ex__(self, proto=0, /)
| Return state information for pickling.
|
| __repr__(self, /)
| Return repr(self).
|
| __rmod__(self, value, /)
| Return value%self.
|
| __rmul__(self, value, /)
| Return value*self.
|
| __setitem__(self, key, value, /)
| Set self[key] to value.
|
| __sizeof__(self, /)
| Returns the size of the bytearray object in memory, in bytes.
|
| __str__(self, /)
| Return str(self).
|
| append(self, item, /)
| Append a single item to the end of the bytearray.
|
| item
| The item to be appended.
|
| capitalize(...)
| B.capitalize() -> copy of B
|
| Return a copy of B with only its first character capitalized (ASCII)
| and the rest lower-cased.
|
| center(self, width, fillchar=b' ', /)
| Return a centered string of length width.
|
| Padding is done using the specified fill character.
|
| clear(self, /)
| Remove all items from the bytearray.
|
| copy(self, /)
| Return a copy of B.
|
| count(...)
| B.count(sub[, start[, end]]) -> int
|
| Return the number of non-overlapping occurrences of subsection sub in
| bytes B[start:end]. Optional arguments start and end are interpreted
| as in slice notation.
|
| decode(self, /, encoding='utf-8', errors='strict')
| Decode the bytearray using the codec registered for encoding.
|
| encoding
| The encoding with which to decode the bytearray.
| errors
| The error handling scheme to use for the handling of decoding errors.
| The default is 'strict' meaning that decoding errors raise a
| UnicodeDecodeError. Other possible values are 'ignore' and 'replace'
| as well as any other name registered with codecs.register_error that
| can handle UnicodeDecodeErrors.
|
| endswith(...)
| B.endswith(suffix[, start[, end]]) -> bool
|
| Return True if B ends with the specified suffix, False otherwise.
| With optional start, test B beginning at that position.
| With optional end, stop comparing B at that position.
| suffix can also be a tuple of bytes to try.
|
| expandtabs(self, /, tabsize=8)
| Return a copy where all tab characters are expanded using spaces.
|
| If tabsize is not given, a tab size of 8 characters is assumed.
|
| extend(self, iterable_of_ints, /)
| Append all the items from the iterator or sequence to the end of the bytearray.
|
| iterable_of_ints
| The iterable of items to append.
|
| find(...)
| B.find(sub[, start[, end]]) -> int
|
| Return the lowest index in B where subsection sub is found,
| such that sub is contained within B[start,end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Return -1 on failure.
|
| hex(...)
| Create a string of hexadecimal numbers from a bytearray object.
|
| sep
| An optional single character or byte to separate hex bytes.
| bytes_per_sep
| How many bytes between separators. Positive values count from the
| right, negative values count from the left.
|
| Example:
| >>> value = bytearray([0xb9, 0x01, 0xef])
| >>> value.hex()
| 'b901ef'
| >>> value.hex(':')
| 'b9:01:ef'
| >>> value.hex(':', 2)
| 'b9:01ef'
| >>> value.hex(':', -2)
| 'b901:ef'
|
| index(...)
| B.index(sub[, start[, end]]) -> int
|
| Return the lowest index in B where subsection sub is found,
| such that sub is contained within B[start,end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Raises ValueError when the subsection is not found.
|
| insert(self, index, item, /)
| Insert a single item into the bytearray before the given index.
|
| index
| The index where the value is to be inserted.
| item
| The item to be inserted.
|
| isalnum(...)
| B.isalnum() -> bool
|
| Return True if all characters in B are alphanumeric
| and there is at least one character in B, False otherwise.
|
| isalpha(...)
| B.isalpha() -> bool
|
| Return True if all characters in B are alphabetic
| and there is at least one character in B, False otherwise.
|
| isascii(...)
| B.isascii() -> bool
|
| Return True if B is empty or all characters in B are ASCII,
| False otherwise.
|
| isdigit(...)
| B.isdigit() -> bool
|
| Return True if all characters in B are digits
| and there is at least one character in B, False otherwise.
|
| islower(...)
| B.islower() -> bool
|
| Return True if all cased characters in B are lowercase and there is
| at least one cased character in B, False otherwise.
|
| isspace(...)
| B.isspace() -> bool
|
| Return True if all characters in B are whitespace
| and there is at least one character in B, False otherwise.
|
| istitle(...)
| B.istitle() -> bool
|
| Return True if B is a titlecased string and there is at least one
| character in B, i.e. uppercase characters may only follow uncased
| characters and lowercase characters only cased ones. Return False
| otherwise.
|
| isupper(...)
| B.isupper() -> bool
|
| Return True if all cased characters in B are uppercase and there is
| at least one cased character in B, False otherwise.
|
| join(self, iterable_of_bytes, /)
| Concatenate any number of bytes/bytearray objects.
|
| The bytearray whose method is called is inserted in between each pair.
|
| The result is returned as a new bytearray object.
|
| ljust(self, width, fillchar=b' ', /)
| Return a left-justified string of length width.
|
| Padding is done using the specified fill character.
|
| lower(...)
| B.lower() -> copy of B
|
| Return a copy of B with all ASCII characters converted to lowercase.
|
| lstrip(self, bytes=None, /)
| Strip leading bytes contained in the argument.
|
| If the argument is omitted or None, strip leading ASCII whitespace.
|
| partition(self, sep, /)
| Partition the bytearray into three parts using the given separator.
|
| This will search for the separator sep in the bytearray. If the separator is
| found, returns a 3-tuple containing the part before the separator, the
| separator itself, and the part after it as new bytearray objects.
|
| If the separator is not found, returns a 3-tuple containing the copy of the
| original bytearray object and two empty bytearray objects.
|
| pop(self, index=-1, /)
| Remove and return a single item from B.
|
| index
| The index from where to remove the item.
| -1 (the default value) means remove the last item.
|
| If no index argument is given, will pop the last item.
|
| remove(self, value, /)
| Remove the first occurrence of a value in the bytearray.
|
| value
| The value to remove.
|
| removeprefix(self, prefix, /)
| Return a bytearray with the given prefix string removed if present.
|
| If the bytearray starts with the prefix string, return
| bytearray[len(prefix):]. Otherwise, return a copy of the original
| bytearray.
|
| removesuffix(self, suffix, /)
| Return a bytearray with the given suffix string removed if present.
|
| If the bytearray ends with the suffix string and that suffix is not
| empty, return bytearray[:-len(suffix)]. Otherwise, return a copy of
| the original bytearray.
|
| replace(self, old, new, count=-1, /)
| Return a copy with all occurrences of substring old replaced by new.
|
| count
| Maximum number of occurrences to replace.
| -1 (the default value) means replace all occurrences.
|
| If the optional argument count is given, only the first count occurrences are
| replaced.
|
| reverse(self, /)
| Reverse the order of the values in B in place.
|
| rfind(...)
| B.rfind(sub[, start[, end]]) -> int
|
| Return the highest index in B where subsection sub is found,
| such that sub is contained within B[start,end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Return -1 on failure.
|
| rindex(...)
| B.rindex(sub[, start[, end]]) -> int
|
| Return the highest index in B where subsection sub is found,
| such that sub is contained within B[start,end]. Optional
| arguments start and end are interpreted as in slice notation.
|
| Raise ValueError when the subsection is not found.
|
| rjust(self, width, fillchar=b' ', /)
| Return a right-justified string of length width.
|
| Padding is done using the specified fill character.
|
| rpartition(self, sep, /)
| Partition the bytearray into three parts using the given separator.
|
| This will search for the separator sep in the bytearray, starting at the end.
| If the separator is found, returns a 3-tuple containing the part before the
| separator, the separator itself, and the part after it as new bytearray
| objects.
|
| If the separator is not found, returns a 3-tuple containing two empty bytearray
| objects and the copy of the original bytearray object.
|
| rsplit(self, /, sep=None, maxsplit=-1)
| Return a list of the sections in the bytearray, using sep as the delimiter.
|
| sep
| The delimiter according which to split the bytearray.
| None (the default value) means split on ASCII whitespace characters
| (space, tab, return, newline, formfeed, vertical tab).
| maxsplit
| Maximum number of splits to do.
| -1 (the default value) means no limit.
|
| Splitting is done starting at the end of the bytearray and working to the front.
|
| rstrip(self, bytes=None, /)
| Strip trailing bytes contained in the argument.
|
| If the argument is omitted or None, strip trailing ASCII whitespace.
|
| split(self, /, sep=None, maxsplit=-1)
| Return a list of the sections in the bytearray, using sep as the delimiter.
|
| sep
| The delimiter according which to split the bytearray.
| None (the default value) means split on ASCII whitespace characters
| (space, tab, return, newline, formfeed, vertical tab).
| maxsplit
| Maximum number of splits to do.
| -1 (the default value) means no limit.
|
| splitlines(self, /, keepends=False)
| Return a list of the lines in the bytearray, breaking at line boundaries.
|
| Line breaks are not included in the resulting list unless keepends is given and
| true.
|
| startswith(...)
| B.startswith(prefix[, start[, end]]) -> bool
|
| Return True if B starts with the specified prefix, False otherwise.
| With optional start, test B beginning at that position.
| With optional end, stop comparing B at that position.
| prefix can also be a tuple of bytes to try.
|
| strip(self, bytes=None, /)
| Strip leading and trailing bytes contained in the argument.
|
| If the argument is omitted or None, strip leading and trailing ASCII whitespace.
|
| swapcase(...)
| B.swapcase() -> copy of B
|
| Return a copy of B with uppercase ASCII characters converted
| to lowercase ASCII and vice versa.
|
| title(...)
| B.title() -> copy of B
|
| Return a titlecased version of B, i.e. ASCII words start with uppercase
| characters, all remaining cased characters have lowercase.
|
| translate(self, table, /, delete=b'')
| Return a copy with each character mapped by the given translation table.
|
| table
| Translation table, which must be a bytes object of length 256.
|
| All characters occurring in the optional argument delete are removed.
| The remaining characters are mapped through the given translation table.
|
| upper(...)
| B.upper() -> copy of B
|
| Return a copy of B with all ASCII characters converted to uppercase.
|
| zfill(self, width, /)
| Pad a numeric string with zeros on the left, to fill a field of the given width.
|
| The original string is never truncated.
|
| ----------------------------------------------------------------------
| Class methods defined here:
|
| fromhex(string, /) from builtins.type
| Create a bytearray object from a string of hexadecimal numbers.
|
| Spaces between two numbers are accepted.
| Example: bytearray.fromhex('B9 01EF') -> bytearray(b'\\xb9\\x01\\xef')
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| maketrans(frm, to, /)
| Return a translation table useable for the bytes or bytearray translate method.
|
| The returned table will be one where each byte in frm is mapped to the byte at
| the same position in to.
|
| The bytes objects frm and to must be of the same length.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __hash__ = None
hash
Notice under data and other attributes that __hash__ = None. The value of a mutable data type is not fixed and therefore does not have a hash value, because it has no has value it cannot be used as a key in a mapping such as a dictionary:
colors = {'red': '#ff0000',
'green': '#00b050',
'blue': '#0070c0'}
colors['red']
'#ff0000'
colors = {bytes('red', encoding='ascii'): '#ff0000',
bytes('green', encoding='ascii'): '#00b050',
bytes('blue', encoding='ascii'): '#0070c0'}
colors[bytes('red', encoding='ascii')]
'#ff0000'
colors = {bytearray('red', encoding='ascii'): '#ff0000',
bytearray('green', encoding='ascii'): '#00b050',
bytearray('blue', encoding='ascii'): '#0070c0'}
colors[bytearray('red', encoding='ascii')]
TypeError: unhashable type: 'bytearray'
Mutable Methods
Supposing the two instances are created:
greeting_r = bytes('Γειά σου Κόσμε!', encoding='UTF-8')
greeting_rw = bytearray('Γειά σου Κόσμε!', encoding='UTF-8')
greeting_r
greeting_rw
b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!'
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!')
In both cases the 2 bytes corresponding to each Greek letters are shown as escape characters and the bytes corresponding to the ASCII characters ' ' and '!' are automatically decoded and displayed. Notice the formal representation for the bytearray involves the byte, indicating how close these two classes are.
The bytes instance is immutable and can be thought of as being read only while the bytearray instance is mutable and can be thought of as being read write. If the data model method __getitem__ is examined:
Signature: greeting_rw.__getitem__(key, /) Call signature: greeting_rw.__getitem__(*args, **kwargs) Type: method-wrapper String form: <method-wrapper '__getitem__' of bytearray object at 0x00000201D230E3F0> Docstring: Return self[key].
The byte at 0 can be indexed from either Sequence using square brackets:
greeting_r[0]
greeting_rw[0]
206
206
Recall this is easier to conceptualise in hexadecimal:
greeting_rw.hex() hex(greeting_rw[0]).removeprefix('0x')
'ce93ceb5ceb9ceac20cf83cebfcf8520ce9acf8ccf83cebcceb521'
'ce'
And:
hex(206)
'0xce'
Slicing can also be used to return a slice
greeting_r[0:2]
greeting_rw[0:2]
b'\xce\x93'
bytearray(b'\xce\x93')
The bytearray also has the equivalent write data model identifiers __setitem__ and __delitem__:
Signature: bytearray.__setitem__(self, key, value, /) Call signature: bytearray.__setitem__(*args, **kwargs) Type: wrapper_descriptor String form: <slot wrapper '__setitem__' of 'bytearray' objects> Namespace: Python builtin Docstring: Set self[key] to value.
Signature: bytearray.__delitem__(self, key, /) Call signature: bytearray.__delitem__(*args, **kwargs) Type: wrapper_descriptor String form: <slot wrapper '__delitem__' of 'bytearray' objects> Namespace: Python builtin Docstring: Delete self[key].
This means that the value at index 0 can be reassigned to another byte:
greeting_rw[0] = 0xde
greeting_rw
bytearray(b'\xde\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!')
If the bytearray is decoded:
greeting_rw.decode(encoding='UTF-8')
'ޓειά σου Κόσμε!'
The first character corresponding to the two bytes b'xde\xce' is replaced by the character corresponding to the two bytes b'xce\xce'.
Attempting to do the same with the bytes will give a TypeError:
greeting_r[0] = 0xde
TypeError: 'bytes' object does not support item assignment
In the bytearray, an individual byte can also be deleted:
del greeting_rw[0]
greeting_rw
bytearray(b'\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!')
Note all subsequent bytes will have their index value lowered by 1.
Attempting to decode this gives a UnicodeDecodeError:
greeting_rw.decode(encoding='UTF-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x93 in position 0: invalid start byte
If the other byte that originally encoded the first character is also deleted:
del greeting_rw[0]
greeting_rw
bytearray(b'\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!')
Now the bytearray can be decoded:
greeting_rw.decode(encoding='UTF-8')
'ειά σου Κόσμε!'
The bytearray also has following mutable methods:
- append
- extend
- insert
- remove
- clear
- reverse
And:
- pop
The mutable methods above all mutate the original bytearray and have no return value. The exception is pop which mutates the original bytearray and returns a value.
append is used to append a single integer to the bytearray. The integer must be <256 and corresponds to the byte. Because the input argument is before a /, it must be provided positionally.
Signature: greeting_rw.append(item, /) Docstring: Append a single item to the end of the bytearray. item The item to be appended. Type: builtin_function_or_method
Supposing the following bytearray is created:
greeting_rw = bytearray('Γειά σου Κόσμε!', encoding='UTF-8')
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!')
The letter 'a' can be appended using its ordinal value of 97:
greeting_rw.append(97)
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!a')
Recall that:
chr(97)
'a'
And:
hex(97)
'0x61'
However as the byte 61 is the ASCII character 'a', 'a' displays instead of \x61 in the above.
Notice what happens if the letter 'b' is appended using its ordinal value and assigned to a result:
result = greeting_rw.append(98)
result
Because the mutable method append has no return value, the value assigned to result is None and this can be seen in the Variable Explorer.

Unfortunately at the time of writing the bytearray is not fully supported within the Variable Explorer. Its contents can be seen by inputting the instance name into an ipython cell:
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!ab')
It is seen to be updated in place.
A simple custom function can be created with a return value:
def fun1():
return 'hello'
If this function is called and assigned to a Variable. The Variable will have the returned value:
greeting = fun1()

If instead the function explicitly has no return value:
def fun2():
return None
If this function is called and assigned to a Variable. The Variable will have the returned value which is explicitly None:
empty = fun2()

The above function can also implicitly have a None value using:
def fun2():
return
Or execute some other lines of code with no return statement:
def fun2():
1 + 2
Some other functions may also use the keyword pass:
def fun2():
pass
Note care needs to be made when using reassignment with a method that has no return value. For example in the case below:
greeting_rw = greeting_rw.append(99)
greeting_rw

greeting_rw has been reassigned to the return value which was None.
The extend method is used to assign an iterable of integers to the bytearray, this iterable is typically another bytearray however can be a byte or a tuple of integers:
Signature: greeting_rw.extend(iterable_of_ints, /) Docstring: Append all the items from the iterator or sequence to the end of the bytearray. iterable_of_ints The iterable of items to append. Type: builtin_function_or_method
Returning to the original bytearray:
greeting_rw = bytearray('Γειά σου Κόσμε!', encoding='UTF-8')
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!')
greeting_rw.extend(b'abc')
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!abc')
The uppercase letters 'A', 'B' and 'C' have an ordinal value of 65, 66 and 67:
greeting_rw.extend((65, 66, 67))
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!abcABC')
The insert method is similar to the append method. An index value where the item to be inserted is required as the first input argument and the item to be inserted is the second input argument. Because the input arguments are before a /, they must be provided positionally.
Signature: greeting_rw.insert(index, item, /) Docstring: Insert a single item into the bytearray before the given index. index The index where the value is to be inserted. item The item to be inserted. Type: builtin_function_or_method
A for loop will be used to view the index and value of each byte in the byte array:
for index, b in enumerate(greeting_rw):
print(f'{index}: {hex(b)}')
0: 0xce
1: 0x93
2: 0xce
3: 0xb5
4: 0xce
5: 0xb9
6: 0xce
7: 0xac
8: 0x20
9: 0xcf
10: 0x83
11: 0xce
12: 0xbf
13: 0xcf
14: 0x85
15: 0x20
16: 0xce
17: 0x9a
18: 0xcf
19: 0x8c
20: 0xcf
21: 0x83
22: 0xce
23: 0xbc
24: 0xce
25: 0xb5
26: 0x21
27: 0x61
28: 0x62
29: 0x63
30: 0x41
31: 0x42
32: 0x43
The letter 'D' can be inserted at index 6 using its ordinal value of 68:
greeting_rw.insert(6, 68)
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9D\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!abcABC')
All bytes at index 6 and subsequent have their index value increased by a value of 1:
for index, b in enumerate(greeting_rw):
print(f'{index}: {hex(b)}')
0: 0xce
1: 0x93
2: 0xce
3: 0xb5
4: 0xce
5: 0xb9
6: 0x44
7: 0xce
8: 0xac
9: 0x20
10: 0xcf
11: 0x83
12: 0xce
13: 0xbf
14: 0xcf
15: 0x85
16: 0x20
17: 0xce
18: 0x9a
19: 0xcf
20: 0x8c
21: 0xcf
22: 0x83
23: 0xce
24: 0xbc
25: 0xce
26: 0xb5
27: 0x21
28: 0x61
29: 0x62
30: 0x63
31: 0x41
32: 0x42
33: 0x43
The hex value of 68 is 0x44 which is seen at index 6:
hex(68)
0x44
The bytearray can be decoded to a Unicode string using:
greeting_rw.decode(encoding='UTF-8')
'ΓειDά σου Κόσμε!abcABC'
The remove method can be used to remove the first occurrence of a value in the bytearray:
Signature: greeting_rw.remove(value, /) Docstring: Remove the first occurrence of a value in the bytearray. value The value to remove. Type: builtin_function_or_method
For example, the letter 'D' can be removed using its ordinal value of 68 and the bytearray can once again be decoded to a Unicode string using UTF-8:
greeting_rw.remove(68)
greeting_rw.decode(encoding='UTF-8')
'Γειά σου Κόσμε!abcABC'
The reverse method can be used to reverse the order of bytes in the bytearray:
Signature: greeting_rw.reverse() Docstring: Reverse the order of the values in B in place. Type: builtin_function_or_method
For example the bytearray instance can be examined:
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!abcABC')
And then reversed:
greeting_rw.reverse()
greeting_rw
bytearray(b'CBAcba!\xb5\xce\xbc\xce\x83\xcf\x8c\xcf\x9a\xce \x85\xcf\xbf\xce\x83\xcf \xac\xce\xb9\xce\xb5\xce\x93\xce')
Attempting to decode the reversed bytearray to a Unicode string using UTF-8 will likely result in an UnicodeDecodeError:
greeting_rw.decode(encoding='UTF-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb5 in position 7: invalid start byte
The bytearray can be cleared using the clear method:
Signature: greeting_rw.clear() Docstring: Remove all items from the bytearray. Type: builtin_function_or_method
greeting_rw.clear()
greeting_rw
bytearray(b'')
This returns an empty bytearray.

Notice the subtle difference between this and the type None:
greeting_rw = greeting_rw.clear()

None is a different data type and does not have the methods of a bytearray.
Let's recreate the bytearray using:
greeting_rw = bytearray('Γειά σου Κόσμε!', encoding='UTF-8')
The method pop can be used to pop of a value using its index:
Signature: greeting_rw.pop(index=-1, /) Docstring: Remove and return a single item from B. index The index from where to remove the item. -1 (the default value) means remove the last item. If no index argument is given, will pop the last item. Type: builtin_function_or_method
The default index is -1, which means the last item is popped off by default:
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5!')
Notice the returned value which is the integer 33:
greeting_rw.pop()
33
Recall that:
chr(33)
'!'
The bytearray is also mutated inplace:
greeting_rw
bytearray(b'\xce\x93\xce\xb5\xce\xb9\xce\xac \xcf\x83\xce\xbf\xcf\x85 \xce\x9a\xcf\x8c\xcf\x83\xce\xbc\xce\xb5')
The index of each value of the bytearray can be examined using:
for index, b in enumerate(greeting_rw):
print(f'{index}: {hex(b)}')
0: 0xce
1: 0x93
2: 0xce
3: 0xb5
4: 0xce
5: 0xb9
6: 0xce
7: 0xac
8: 0x20
9: 0xcf
10: 0x83
11: 0xce
12: 0xbf
13: 0xcf
14: 0x85
15: 0x20
16: 0xce
17: 0x9a
18: 0xcf
19: 0x8c
20: 0xcf
21: 0x83
22: 0xce
23: 0xbc
24: 0xce
25: 0xb5
Normally the popped value is assigned to a variable name. The value at index 6 can for example be popped and assigned to a new variable name:
popped_value = greeting_rw.pop(6)
popped_value
172
This has a hex value of:
hex(popped_value)
'0xce'
And the original bytearray is mutated:
for index, b in enumerate(greeting_rw):
print(f'{index}: {hex(b)}')
0: 0xce
1: 0x93
2: 0xce
3: 0xb5
4: 0xce
5: 0xb9
6: 0x20
7: 0xcf
8: 0x83
9: 0xce
10: 0xbf
11: 0xcf
12: 0x85
13: 0x20
14: 0xce
15: 0x9a
16: 0xcf
17: 0x8c
18: 0xcf
19: 0x83
20: 0xce
21: 0xbc
22: 0xce
23: 0xb5