efficient arrays of booleans for Python
This library provides an object type which efficiently represents an array of booleans. Bitarrays are sequence types and behave very much like usual lists. Eight bits are represented by one byte in a contiguous block of memory. The user can select between two representations: little-endian and big-endian. All functionality is implemented in C. Methods for accessing the machine representation are provided, including the ability to import and export buffers. This allows creating bitarrays that are mapped to other objects, including memory-mapped files.
The bit-endianness can be specified for each bitarray object, see below.
Sequence methods: slicing (including slice assignment and deletion),
operations +, *, +=, *=, the in operator, len()
Bitwise operations: ~, &, |, ^, > (as well as
their in-place versions &=, |=, ^=, >=).
Free-threading support (for GIL-disabled CPython 3.14 and later)
Fast methods for encoding and decoding variable-length prefix codes.
Bitarray objects support the buffer protocol (both importing and exporting buffers).
Efficient packing and unpacking for interoperability with byte-oriented objects such as NumPy arrays.
Pickling and unpickling of bitarray objects.
Immutable frozenbitarray objects which are hashable
Fast searching for bits and bit patterns
Type hinting
Extensive test suite with over 600 unit tests
Utility module bitarray.util:
Python wheels are are available on PyPI for all major platforms and Python versions. Which means you can simply:
.. code-block:: shell-session
$ pip install bitarrayOnce you have installed the package, you may want to test it:
.. code-block:: shell-session
$ python -c 'import bitarray; bitarray.test()'
bitarray is installed in: /Users/ilan/bitarray/bitarray
sys.prefix: /Users/ilan/miniforge
bitarray version: 3.11.0
sys.version: 3.14.5 (main, May 20 2026) [Clang 20.1.8]
sys.abiflags: ''
sys._is_gil_enabled(): True
pointer size: 64 bit
sizeof(size_t): 8
sizeof(bitarrayobject): 80
HAVE_BUILTIN_BSWAP64: 1
default bit-endianness: big
machine byte-order: little
Py_GIL_DISABLED: 0
Py_DEBUG: 0
DEBUG: 0
.....................................s...................................
..........s.....................................s........................
......s.........................................................
----------------------------------------------------------------------
Ran 654 tests in 0.192s
OK (skipped=4)The test() function is part of the API. It will return
a unittest.runner.TextTestResult object, such that one can verify that
all tests ran successfully by:
.. code-block:: python
import bitarray
assert bitarray.test().wasSuccessful()As mentioned above, bitarray objects behave very much like lists, so there is not too much to learn. The biggest difference from list objects (except that bitarray are obviously homogeneous) is the ability to access the machine representation of the object. When doing so, the bit-endianness is of importance; this issue is explained in detail in the section below. Here, we demonstrate the basic usage of bitarray objects:
.. code-block:: python
>>> from bitarray import bitarray
>>> a = bitarray() # create empty bitarray
>>> a.append(1)
>>> a.extend([1, 0])
>>> a
bitarray('110')
>>> x = bitarray(2 ** 20) # bitarray of length 1048576 (initialized to 0)
>>> len(x)
1048576
>>> bitarray('1001 011') # initialize from string (whitespace is ignored)
bitarray('1001011')
>>> lst = [1, 0, False, True, True]
>>> a = bitarray(lst) # initialize from iterable
>>> a
bitarray('10011')
>>> a[2] # indexing a single item will always return an integer
0
>>> a[2:4] # whereas indexing a slice will always return a bitarray
bitarray('01')
>>> a[2:3] # even when the slice length is just one
bitarray('0')
>>> a.count(1)
3
>>> a.remove(0) # removes first occurrence of 0
>>> a
bitarray('1011')Like lists, bitarray objects support slice assignment and deletion:
.. code-block:: python
>>> a = bitarray(50)
>>> a.setall(0) # set all elements in a to 0
>>> a[11:37:3] = 9 * bitarray('1')
>>> a
bitarray('00000000000100100100100100100100100100000000000000')
>>> del a[12::3]
>>> a
bitarray('0000000000010101010101010101000000000')
>>> a[-6:] = bitarray('10011')
>>> a
bitarray('000000000001010101010101010100010011')
>>> a += bitarray('000111')
>>> a[9:]
bitarray('001010101010101010100010011000111')In addition, slices can be assigned to booleans, which is easier (and faster) than assigning to a bitarray in which all values are the same:
.. code-block:: python
>>> a = 20 * bitarray('0')
>>> a[1:15:3] = True
>>> a
bitarray('01001001001001000000')This is easier and faster than:
.. code-block:: python
>>> a = 20 * bitarray('0')
>>> a[1:15:3] = 5 * bitarray('1')
>>> a
bitarray('01001001001001000000')Note that in the latter we have to create a temporary bitarray whose length must be known or calculated. Another example of assigning slices to Booleans, is setting ranges:
.. code-block:: python
>>> a = bitarray(30)
>>> a[:] = 0 # set all elements to 0 - equivalent to a.setall(0)
>>> a[10:25] = 1 # set elements in range(10, 25) to 1
>>> a
bitarray('000000000011111111111111100000')As of bitarray version 2.8, indices may also be lists of arbitrary
indices (like in NumPy), or bitarrays that are treated as masks,
see Bitarray indexing __.
Bitarray objects support the bitwise operators ~, &, |, ^,
> (as well as their in-place versions &=, |=, ^=,
>=). The behavior is very much what one would expect:
.. code-block:: python
>>> a = bitarray('101110001')
>>> ~a # invert
bitarray('010001110')
>>> b = bitarray('111001011')
>>> a ^ b # bitwise XOR
bitarray('010111010')
>>> a &= b # inplace AND
>>> a
bitarray('101000001')
>>> a >> a
bitarray('100000100')
>>> b >> 1 # return b right-shifted by 1
bitarray('011100101')The C language does not specify the behavior of negative shifts and of left shifts larger or equal than the width of the promoted left operand. The exact behavior is compiler/machine specific. This Python bitarray library specifies the behavior as follows:
ValueErrorIt is worth noting that (regardless of bit-endianness) the bitarray left
shift (>) always shifts towards higher indices.
For many purposes the bit-endianness is not of any relevance to the end user
and can be regarded as an implementation detail of bitarray objects.
However, there are use cases when the bit-endianness becomes important.
These use cases involve explicitly reading and writing the bitarray buffer
using .tobytes(), .frombytes(), .tofile() or .fromfile(),
importing and exporting buffers. Also, a number of utility functions
in bitarray.util will return different results depending on
bit-endianness, such as ba2hex() or ba2int.
To better understand this topic, please read bit-endianness __.
Bitarray objects support the buffer protocol. They can both export their
own buffer, as well as import another object's buffer. To learn more about
this topic, please read buffer protocol . There is also an example that shows how
to memory-map a file to a bitarray: mmapped-file.py
The .encode() method takes a dictionary mapping symbols to bitarrays
and an iterable, and extends the bitarray object with the encoded symbols
found while iterating. For example:
.. code-block:: python
>>> d = {'H':bitarray('111'), 'e':bitarray('0'),
... 'l':bitarray('110'), 'o':bitarray('10')}
...
>>> a = bitarray()
>>> a.encode(d, 'Hello')
>>> a
bitarray('111011011010')Note that the string 'Hello' is an iterable, but the symbols are not
limited to characters, in fact any immutable Python object can be a symbol.
Taking the same dictionary, we can apply the .decode() method which will
return an iterable of the symbols:
.. code-block:: python
>>> list(a.decode(d))
['H', 'e', 'l', 'l', 'o']
>>> ''.join(a.decode(d))
'Hello'Symbols are not limited to being characters.
The above dictionary d can be efficiently constructed using the function
bitarray.util.huffman_code(). I also wrote Huffman coding in Python using bitarray __ for more
background information.
When the codes are large, and you have many decode calls, most time will
be spent creating the (same) internal decode tree objects. In this case,
it will be much faster to create a decodetree object, which can be
passed to bitarray's .decode() method, instead of passing the prefix
code dictionary to those methods itself:
.. code-block:: python
>>> from bitarray import bitarray, decodetree
>>> t = decodetree({'a': bitarray('0'), 'b': bitarray('1')})
>>> a = bitarray('0110')
>>> list(a.decode(t))
['a', 'b', 'b', 'a']The sole purpose of the immutable decodetree object is to be passed
to bitarray's .decode() method.
A frozenbitarray object is very similar to the bitarray object.
The difference is that this a frozenbitarray is immutable, and hashable,
and can therefore be used as a dictionary key:
.. code-block:: python
>>> from bitarray import frozenbitarray
>>> key = frozenbitarray('1100011')
>>> {key: 'some value'}
{frozenbitarray('1100011'): 'some value'}
>>> key[3] = 1
Traceback (most recent call last):
...
TypeError: frozenbitarray is immutablebitarray version: 3.11.0 -- change log __
In the following, item and value are usually a single bit -
an integer 0 or 1.
Also, sub_bitarray refers to either a bitarray, or an item.
bitarray(initializer=0, /, endian='big', buffer=None) -> bitarray
Return a new bitarray object whose items are bits initialized from
the optional initializer, and bit-endianness.
The initializer may be one of the following types:
a.) int bitarray, initialized to zeros, of given length
b.) bytes or bytearray to initialize buffer directly
c.) str of 0s and 1s, ignoring whitespace and "_"
d.) iterable of integers 0 or 1.
Optional keyword arguments:
endian: Specifies the bit-endianness of the created bitarray object.
Allowed values are big and little (the default is big).
The bit-endianness affects the buffer representation of the bitarray.
buffer: Any object which exposes a buffer. When provided, initializer
cannot be present (or has to be None). The imported buffer may be
read-only or writable, depending on the object type.
New in version 2.3: optional buffer argum
No open issues yet, or sync has not completed.