Note
The Array class is new in version 4.1 of bitstring, and is considered a ‘beta’ feature for now. There may be some small changes in future point releases and it hasn’t been tested as well as the rest of the library.
This documentation may also be a bit ‘beta’.
Array Class¶
- class Array(fmt: str[, initializer[, trailing_bits]])¶
Create a new
Array
whose elements are set by thefmt
string. This can be any format which has a fixed length. See Format tokens and Compact format strings for details on allowed format strings, noting that only formats with well defined bit lengths are allowed.The
Array
class is a way to efficiently store data that has a single type with a set length. Thebitstring.Array
type is meant as a more flexible version of the standardarray.array
, and can be used the same way.import array import bitstring x = array.array('f', [1.0, 2.0, 3.14]) y = bitstring.Array('=f', [1.0, 2.0, 3.14]) assert x.tobytes() == y.tobytes()
This example packs three 32-bit floats into objects using both libraries. The only difference is the explicit native endianness for the format string of the bitstring version. The bitstring Array’s advantage lies in the way that any fixed-length bitstring format can be used instead of just the dozen or so typecodes supported by the
array
module.For example
'uint4'
,'bfloat'
or'hex12'
can be used, and the endianness of multi-byte formats can be properly specified.Each element in the
Array
must then be something that makes sense for thefmt
. Some examples will help illustrate:from bitstring import Array # Each unsigned int is stored in 4 bits a = Array('uint4', [0, 5, 5, 3, 2]) # Convert and store floats in 8 bits each b = Array('float8_152', [-56.0, 0.123, 99.6]) # Each element is a 7 bit signed integer c = Array('int7', [-3, 0, 120])
You can then access and modify the
Array
with the usual notation:a[1:4] # Array('uint4', [5, 5, 3]) b[0] # -56.0 c[-1] # 120 a[0] = 2 b.extend([0.0, -1.5])
Conversion between
Array
types can be done by creating a new one with the new format from the elements of the other one. If elements of the old array don’t fit or don’t make sense in the new array then the relevant exceptions will be raised.>>> x = Array('float64', [89.3, 1e34, -0.00000001, 34]) >>> y = Array('float16', x.tolist()) >>> y Array('float16', [89.3125, inf, -0.0, 34.0]) >>> y = Array('float8_143', y.tolist()) >>> y Array('float8_143', [88.0, 240.0, 0.0, 32.0]) >>> Array('uint8', y.tolist()) Array('uint8', [88, 240, 0, 32]) >>> Array('uint7', y.tolist()) bitstring.CreationError: 240 is too large an unsigned integer for a bitstring of length 7. The allowed range is [0, 127].
You can also reinterpret the data by changing the
fmt
property directly. This will not copy any data but will cause the current data to be shown differently.>>> x = Array('int16', [-5, 100, -4]) >>> x Array('int16', [-5, 100, -4]) >>> x.fmt = 'int8' >>> x Array('int8', [-1, -5, 0, 100, -1, -4])
The data for the array is stored internally as a
BitArray
object. It can be directly accessed using thedata
property. You can freely manipulate the internal data using all of the methods available for theBitArray
class.The
Array
object also has atrailing_bits
read-only data member, which consists of the end bits of thedata
BitArray
that are left over when theArray
is interpreted usingfmt
. Typicallytrailing_bits
will be an emptyBitArray
but if you change the length of thedata
or change thefmt
specification there may be some bits left over.Some methods, such as
append
andextend
will raise an exception if used whentrailing_bits
is not empty, as it not clear how these should behave in this case. You can however still useinsert
which will always leave thetrailing_bits
unchanged.The
fmt
string can be a type code such as'>H'
or'=d'
but it can also be a string defining any format which has a fixed-length in bits, for example'int12'
,'bfloat'
,'bytes5'
or'bool'
.Note that the typecodes must include an endianness character to give the byte ordering. This is more like the
struct
module typecodes, and is different to thearray.array
typecodes which are always native-endian.The correspondence between the big-endian type codes and bitstring format codes is given in the table below.
Type code
bitstring format
'>b'
'int8'
'>B'
'uint8'
'>h'
'int16'
'>H'
'uint16'
'>l'
'int32'
'>L'
'uint32'
'>q'
'int64'
'>Q'
'uint64'
'>e'
'float16'
'>f'
'float32'
'>d'
'float64'
The endianness character can be
'>'
for big-endian,'<'
for little-endian or'='
for native-endian ('@'
can also be used for native-endian). In the bitstring formats the default is big-endian, but you can specify little or native endian using'le'
or'ne'
modifiers, for example:Type code
bitstring format
'>H'
'uint16'
/'uintbe16'
'=H'
'uintne16'
'<H'
'uintle16'
Note that:
The
array
module’s native endianness means that different packed binary data will be created on different types of machines. Users may find that behaviour unexpected which is why endianness must be explicitly given as in the rest of the bitstring module.The
'u'
type code from thearray
module isn’t supported as its length is platform dependent.The
'e'
type code isn’t one of thearray
supported types, but it is used in thestruct
module and we support it here.The
'b'
and'B'
type codes need to be preceded by an endianness character even though it makes no difference which one you use as they are only 1 byte long.
Methods¶
Note
Some methods that are available for
array.array
objects are deliberately omitted in this interface as they don’t really add much. In particular, some omissions and their suggested replacements are:
a.fromlist(alist)
→a.extend(alist)
a.frombytes(s)
→a.data.extend(s)
- Array.append(x: float | int | str | bytes) None ¶
Add a new element with value x to the end of the Array. The type of x should be appropriate for the type of the Array.
Raises a
ValueError
if the Array’s bit length is not a multiple of its format length (seetrailing_bits
).
- Array.byteswap() None ¶
Change the byte endianness of each element.
Raises a
ValueError
if the format is not an integer number of bytes long.>>> a = Array('uint32', [100, 1, 999]) >>> a.byteswap() >>> a Array('uint32', [1677721600, 16777216, 3875733504]) >>> a.fmt = 'uintle32' >>> a Array('uintle32', [100, 1, 999])
- Array.count(value: float | int | str | bytes) int ¶
Returns the number of elements set to value.
>>> a = Array('hex4') >>> a.data += '0xdeadbeef' >>> a Array('hex4', ['d', 'e', 'a', 'd', 'b', 'e', 'e', 'f']) >>> a.count('e') 3For floating point types using a value of
float('nan')
will count the number of elements for whichmath.isnan()
returnsTrue
.
- Array.extend(iterable: Iterable | Array) None ¶
Extend the Array by constructing new elements from the values in a list or other iterable.
The iterable can be another
Array
or anarray.array
, but only if the format is the same.>>> a = Array('int5', [-5, 0, 10]) >>> a.extend([3, 2, 1]) >>> a.extend(a[0:3] // 5) >>> a Array('int5', [-5, 0, 10, 3, 2, 1, -1, 0, 2])
- Array.fromfile(f: BinaryIO, n: int | None) None ¶
Append items read from a file object.
- Array.insert(i: int, x: float | int | str | bytes) None ¶
Insert an item at a given position.
>>> a = Array('float8_152', [-10, -5, -0.5, 5, 10]) >>> a.insert(3, 0.5) >>> a Array('float8_152', [-10.0, -5.0, -0.5, 0.5, 5.0, 10.0])
- Array.pop(i: int | None) float | int | str | bytes ¶
Remove and return the item at position i.
If a position isn’t specified the final item is returned and removed.
>>> Array('bytes3', [b'ABC', b'DEF', b'ZZZ']) >>> a.pop(0) b'ABC' >>> a.pop() b'ZZZ' >>> a.pop() b'DEF'
- Array.pp(fmt: str | None, width: int, show_offset: bool, stream: TextIO) None ¶
Pretty print the Array.
fmt defaults to the Array’s current format, but any other valid Array format string, or pair of comma-separated format strings can be used.
The output will try to stay within width characters per line, but will always output at least one element value.
Setting show_offset to
True
will add a element index to each line of the output.An output stream can be specified. This should be an object with a
write
method and the default issys.stdout
.>>> a = Array('u20', bytearray(range(100))) >>> a.pp(width=70) <Array fmt='u20', length=40, itemsize=20 bits, total data size=100 bytes> [ 16 131844 20576 460809 41136 789774 61697 70163 82257 399128 102817 728093 123378 8482 143938 337447 164498 666412 185058 995377 205619 275766 226179 604731 246739 933696 267300 214085 287860 543050 308420 872015 328981 152404 349541 481369 370101 810334 390662 90723 ]>>> a.pp('hex32', width=70) <Array fmt='hex32', length=25, itemsize=32 bits, total data size=100 bytes> [ 00010203 04050607 08090a0b 0c0d0e0f 10111213 14151617 18191a1b 1c1d1e1f 20212223 24252627 28292a2b 2c2d2e2f 30313233 34353637 38393a3b 3c3d3e3f 40414243 44454647 48494a4b 4c4d4e4f 50515253 54555657 58595a5b 5c5d5e5f 60616263 ]>>> a.pp('i12, hex', show_offset=True, width=70) <Array fmt='i12, hex', length=114, itemsize=7 bits, total data size=100 bytes> [ 0 258 48 1029 96 1800 : 000 102 030 405 060 708 144 -1525 192 -754 241 17 : 090 a0b 0c0 d0e 0f1 011 289 788 337 1559 385 -1766 : 121 314 151 617 181 91a 433 -995 481 -224 530 547 : 1b1 c1d 1e1 f20 212 223 578 1318 626 -2007 674 -1236 : 242 526 272 829 2a2 b2c 722 -465 771 306 819 1077 : 2d2 e2f 303 132 333 435 867 1848 915 -1477 963 -706 : 363 738 393 a3b 3c3 d3e 1012 65 1060 836 1108 1607 : 3f4 041 424 344 454 647 1156 -1718 1204 -947 1252 -176 : 484 94a 4b4 c4d 4e4 f50 1301 595 1349 1366 1397 -1959 : 515 253 545 556 575 859 1445 -1188 1493 -417 1542 354 : 5a5 b5c 5d5 e5f 606 162 ] + trailing_bits = 0x63
- Array.reverse() None ¶
Reverse the order of all items in the Array.
>>> a = Array('>L', [100, 200, 300]) >>> a.reverse() >>> a Array('>L', [300, 200, 100])
- Array.tobytes() bytes ¶
Return Array data as bytes object, padding with zero bits at the end if needed.
>>> a = Array('i4', [3, -6, 2, -3, 2, -7]) >>> a.tobytes() b':-)'
- Array.tofile(f: BinaryIO) None ¶
Write Array data to a file, padding with zero bits at the end if needed.
- Array.tolist() List[float | int | str | bytes] ¶
Return Array items as a list.
Each packed element of the Array is converted to an ordinary Python object such as a
float
or anint
depending on the Array’s format, and returned in a Python list.This can be helpful if you want to use an Array to create a new Array with a different format.
>>> a = Array('float16', b'some_long_byte_data?') >>> a Array('float16', [15224.0, 5524.0, 475.0, 7608.0, 1887.0, 828.5, 18000.0, 473.0, 698.0, 671.5]) >>> b = Array('float8_152', a.tolist()) >>> b Array('float8_152', [14336.0, 5120.0, 448.0, 7168.0, 1792.0, 768.0, 16384.0, 448.0, 640.0, 640.0]) >>> b.tobytes() b'wqcskfxcee'
Special Methods¶
- Array.__len__(self) int ¶
len(a)
Return the number of elements in the Array.
>>> a = Array('uint20', [1, 2, 3]) >>> len(a) 3 >>> a.fmt = 'uint1' >>> len(a) 60
- Array.__eq__(self, other) bool ¶
a1 == a2
Equality test - other can be either another bitstring Array or an
array
. To be equal the formats must be equivalent and the underlying bit data must be the same.>>> a = Array('u8', [1, 2, 3, 2, 1]) >>> a[0:3] == a[-1:-4:-1] TrueTo compare only the values contained in the Array, extract them using
tolist
first.
- Array.__ne__(self, other) bool ¶
a1 != a2
- Array.__getitem__(self, key: int | slice) float | int | str | bytes | Array ¶
a[i]
a[start:end:step]
- Array.__setitem__(self, key: int | slice, value) None ¶
a[i] = x
a[start:end:step] = x
- Array.__delitem__(self, key: int | slice) None ¶
del a[i]
del[start:end:step]
Properties¶
- Array.data¶
The bit data of the
Array
, as aBitArray
. Read and write, and can be freely manipulated with all ofBitArray
methods.Note that some
Array
methods such asappend
andextend
require thedata
to have a length that is a multiple of theArray
’sitemsize
.
- Array.fmt¶
The format string used to initialise the
Array
type. Read and write.Changing the format for an already formed
Array
will cause all of the bit data to be reinterpreted and can change the length of theArray
. However, changing the format won’t change the underlying bit data in any way.Note that some
Array
methods such asappend
andextend
require the bit data to have a length that is a multiple of theArray
’sitemsize
.
- Array.itemsize¶
The size in bits of each item in the
Array
. Read-only.Note that this gives a value in bits, unlike the equivalent in the
array
module which gives a value in bytes.>>> a = Array('>h') >>> b = Array('bool') >>> a.itemsize 16 >>> b.itemsize 1
- Array.trailing_bits¶
A
BitArray
object equal to the end of thedata
that is not a multiple of theitemsize
. Read only.This will typically be an empty
BitArray
, but if an thefmt
or thedata
of anArray
object has been altered after its creation then there may be left-over bits at the end of the data.Note that any methods that append items to the
Array
will fail with aValueError
if there are any trailing bits.