mrkeyoor.com_
Wed 23 Sept 00:32 UTC
PyPIDataupdated 22 Sept 2026

bitarray review

bitarray 3.10.1 is a Python sequence that packs eight Boolean values into each byte and runs its core operations in compiled C. It behaves enough like a list to support indexing, slices, deletion, concatenation, and mutation, then adds bitwise operators, buffer access, prefix-code helpers, integer conversion, sparse encoding, and hashable frozenbitarray values. Our Python 3.12 install imported in 0.05 seconds and shipped py.typed with no direct dependencies. Version 3.10.1 fixes narrow-width pretty printing and simplifies integer conversion internals; the preceding 3.10.0 release added free-threaded CPython 3.14 support and public decode-tree inspection.

Verdict

bitarray 3.10.1 installed in 0.2 seconds, used 2 MB, imported in 0.05 seconds, and produced 0 audit findings in our sandbox, making it an easy dependency for dense mutable bits. Walk away when the data is sparse, the format needs named fields, or you cannot store bit endianness and exact length with serialized bytes.

We installed it

Lab card: what happened when we installed bitarrayScreenshot of bitarray documentation
Install✓ · 0.2s1 package on disk · 2 MB
Importimport bitarray in 0.05s · compiled extensions · py.typed · requires Python >=3.7
Known vulns0(pip-audit)

Answers from our run

Does bitarray install cleanly?

Yes. In a fresh container with an empty cache, pip install bitarray finished in 0.2s, leaving 1 package and 2 MB on disk. pip-audit reported no known vulnerabilities.

What does bitarray need to run?

Python >=3.7, and a platform wheel with compiled extensions. In our run import bitarray succeeded in 0.05s, and the package ships py.typed for type checkers.

bitarray or bitstring: which should you use?

bitstring: Use it when readable binary parsing, bit-position cursors, and mixed-width field interpretation matter more than a minimal mutable bitmap. bitarray 3.10.1 installed in 0.2 seconds, used 2 MB, imported in 0.05 seconds, and produced 0 audit findings in our sandbox, making it an easy dependency for dense mutable bits.

When should you not use bitarray?

The set bits are sparse across a huge integer range. PyRoaring stores compressed integer sets without allocating every zero between the smallest and largest member.

API stability4/5Version 3 keeps the list-like constructor, slices, bitwise operators, byte conversion, buffer protocol, frozenbitarray, and utility module as the main working surface. The important major-version break is explicit in the reference: search() and decode() return iterators from 3.0 onward. Releases also add callable surface, including rotate() in 3.9 and decode-tree methods in 3.10, so pinning and running expression-level tests still pays off for code that uses more than the sequence basics.
Docs5/5The README documents constructor forms, every public method and descriptor, the utility module, prefix codes, frozenbitarray, and the buffer protocol with executable examples. It states exact edge behavior for negative and oversized shifts, iterator returns, pad bits, read-only imported buffers, and endianness. Separate documents cover indexing, memory-mapped buffers, random generation, sparse compression, and bit endianness. The material is dense, but the edge cases needed for a binary format are present and searchable.
Maintenance5/5PyPI published 3.10.1 on August 7, 2026, and GitHub records a push on August 24, 2026. The repository is unarchived with 8 open issues and pull requests combined. Recent work includes free-threaded CPython critical sections, a fix from a C extension analysis report, Python 3.14 wheels, mask-indexing optimization, and corrected random generation. That is active platform and correctness work in the C layer, rather than version bumps limited to packaging metadata.
Ecosystem4/5The current package data records 5,086,514 weekly downloads, and GitHub reports 791 stars. PyPI publishes wheels for CPython 3.7 through 3.14 across macOS, Windows, manylinux, and musllinux variants, including free-threaded 3.14 builds. The buffer protocol lets arrays connect to memoryview, memory-mapped files, and NumPy without inventing an adapter format. Its surrounding plugin ecosystem is small because most of the useful interoperation happens through Python's standard sequence and buffer contracts.

Use it if

  • A dense flag vector or bitmap needs one stored bit per value and fast count, search, mask, shift, union, or intersection operations.
  • Binary data must move through bytes, files, memory maps, memoryview, or another object that implements Python's buffer protocol.
  • Prefix-code work needs Huffman code construction, reusable decode trees, or canonical decoding without a separate codec package.
  • Immutable bit sequences need to act as dictionary keys through frozenbitarray while mutable working copies use the same operation model.
Skip it if

Setup reality

Our fresh Python 3.12 sandbox installed bitarray 3.10.1 in 0.2 seconds. One package occupied 2 MB on disk, import completed in 0.05 seconds, and pip-audit found 0 known vulnerabilities. It has 0 direct dependencies, requires Python 3.7 or newer, includes py.typed, and uses compiled .so extensions under the PSF-2.0 license. PyPI supplies wheels for the listed CPython platforms; an unlisted target falls back to a C build.

No credentials or config file are involved. The setup choice is representation: every array uses big or little bit endianness, with big as the default. That setting controls how byte, file, buffer, hex, and integer conversions interpret bits. Machine byte order is a separate concern. Record the chosen bit endianness beside persisted data instead of relying on a process default.

tobytes() zero-fills unused positions in the last byte, and frombytes() restores 8 bits for each input byte. A 13-bit value therefore cannot recover its original length from raw bytes alone. serialize() and deserialize() retain the library's bitarray representation when both endpoints use this package. search() and decode() return iterators in version 3, so wrap them with list() only when materializing every result is intentional.

A bitarray imported from another buffer can be read-only, and exported buffers can prevent resizing while a view exists. Bitwise operands need equal lengths. Shifts keep the original length, fill vacated positions with zero, and turn the whole array to zero when the shift count reaches its length. Free-threaded CPython support arrived in 3.10.0, but shared mutation still needs an application-level ownership rule if several threads can change the same array.

Patterns

Build and edit a bit sequence create-and-mutate

from bitarray import bitarray

bits = bitarray('1001 011')
bits.append(0)
bits[1:4] = bitarray('111')
bits[5:] = False

Whitespace is ignored in a string initializer. Slice assignment with one Boolean fills the selected positions without creating a temporary bitarray.

Intersect and invert equal-length masks combine-masks

from bitarray import bitarray

allowed = bitarray('11010100')
selected = bitarray('10111100')
visible = allowed & selected
hidden = ~visible

Bitwise operands must have the same length. Inversion and shifts keep that length rather than adding positions.

Iterate over active indices find-set-bits

from bitarray import bitarray

flags = bitarray('00101001')
for index in flags.search(1):
    print(index)

search() returns an iterator in version 3. Searching for 1 is the documented fast path for active indices.

Round-trip a byte-aligned value convert-bytes

from bitarray import bitarray

bits = bitarray('10110010', endian='big')
raw = bits.tobytes()
copy = bitarray(endian='big')
copy.frombytes(raw)
assert copy == bits

This round trip is exact because the input has 8 bits. Raw bytes do not preserve the logical length of a partial final byte.

Preserve bitarray representation serialize-with-metadata

from bitarray import bitarray
from bitarray.util import deserialize, serialize

bits = bitarray('10101', endian='little')
payload = serialize(bits)
restored = deserialize(payload)
assert restored == bits
assert restored.endian == 'little'

serialize() records the package representation, including the bit details needed by deserialize(). Use a documented application format for cross-language storage.

Encode and decode a fixed-width integer convert-integer

from bitarray.util import ba2int, int2ba

bits = int2ba(37, length=8, endian='big')
assert bits.to01() == '00100101'
assert ba2int(bits) == 37

length fixes the field width and raises when the integer does not fit. Version 3.10.1 simplified the conversion internals without changing this public call shape.

Use immutable bits as a dictionary key use-hashable-bits

from bitarray import frozenbitarray

opcode = frozenbitarray('1100011')
handlers = {opcode: 'load'}
assert handlers[frozenbitarray('1100011')] == 'load'

frozenbitarray is immutable and hashable. A mutating operation on it raises TypeError.

View an existing byte buffer map-existing-buffer

from bitarray import bitarray

raw = bytearray([0b10100000])
bits = bitarray(endian='big', buffer=raw)
bits[1] = 1
assert raw[0] == 0b11100000

The bitarray imports the supplied buffer instead of copying it. Mutations are shared, and a read-only source produces a read-only view.

Encode and decode symbols encode-prefix-code

from bitarray import bitarray, decodetree

code = {
    'A': bitarray('0'),
    'B': bitarray('10'),
    'C': bitarray('11'),
}
encoded = bitarray()
encoded.encode(code, 'ABCA')
text = ''.join(encoded.decode(decodetree(code)))
assert text == 'ABCA'

decode() returns an iterator. A reusable decodetree avoids rebuilding the prefix tree for repeated payloads.

Rotate a sequence in place rotate-bits

from bitarray import bitarray

bits = bitarray('10010')
bits.rotate(2)
assert bits == bitarray('10100')

rotate() was added in version 3.9 and mutates the array in place. Positive values rotate to the right.

Encode a sparse bitarray compress-sparse-bits

from bitarray import bitarray
from bitarray.util import sc_decode, sc_encode

bits = bitarray(10_000)
bits.setall(0)
bits[[3, 900, 9_999]] = 1
encoded = sc_encode(bits)
assert sc_decode(iter(encoded)) == bits

sc_encode() targets sparse bitarrays. Benchmark it against the real density and storage format before adopting it as a wire contract.

Run the package self-test validate-install

import bitarray

result = bitarray.test()
assert result.wasSuccessful()

test() is documented as public API and returns unittest.runner.TextTestResult. It is useful after a source build or on an unusual architecture.

Alternatives

PackageRegistryPick it when
bitstringPyPIUse it when readable binary parsing, bit-position cursors, and mixed-width field interpretation matter more than a minimal mutable bitmap.
bitstructPyPIUse it when a struct-style format string should define signed integers, floats, padding, and byte or bit order.
numpyPyPIUse it for multidimensional Boolean arrays, broadcasting, axis operations, and integration with numerical code.
pyroaringPyPIUse it for sparse integer sets that benefit from compressed bitmap containers and set-oriented iteration.

More data guides

numpy · fsspec · pandas · sqlalchemy · pyarrow · lxml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.