How to encode stream of bits (not bytes) in Python - is any simple module for it?
Question: I want encode and decode **variable** and **countable** stream of **bits**
into **binary string** , **number** , **64 bases encoded string**. Maximum
length of stream will be about 21 + 20 = 41 bits but can be little longer 43,
45.

Let assume that bits will be represented by some array.

    
    
    bits = [1]
    encoded = someEncoder(bits)
    decoded = someDecoder(encoded)
    assert bits == decoded
    

The stream of bits can be longer for example:

    
    
    import random
    
    def generateRandomBits(l):
      bitsBytes = random.getrandbits(l)
      bits = []
      for i in range(l):
        bitsBytes
        bits.append(int(bitsBytes >> i & 1))
      return bits
    
    bits = generateRandomBits(21)
    # [0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1]
    

Consider that it is **variable** list of bits and encode/decode must be
**lossless** :

    
    
    a = someEncoder([0])
    b = someEncoder([0, 0])
    assert a != b
    


Answer: To turn integers, representing bits, into 8-bit bytes (still as integers), use
bit shifting:

    
    
    result = 0
    for bit in bits:
        result = result << 1 | bit
    

or with `reduce()`:

    
    
    result = reduce(lambda n, b: n << 1 | b, bits)
    

This can be turned into a binary string with `format(result, 'b')`, optionally
with width and `0` prefixed to pad with zeros to a certain length.

Demo:

    
    
    >>> bits = [0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1]
    >>> reduce(lambda n, b: n << 1 | b, bits)
    934809
    >>> result = 0
    >>> for bit in bits:
    ...     result = result << 1 | bit
    ... 
    >>> result
    934809
    >>> format(result, '021b')
    '011100100001110011001'
    

If you need to go straight to the binary string, just map the bits to strings
and join:

    
    
    >>> ''.join(map(str, bits))
    '011100100001110011001'
    

Padding this further to 64 characters could be done with `str.zfill()`:

    
    
    >>> ''.join(map(str, bits)).zfill(64)
    '0000000000000000000000000000000000000000000011100100001110011001'
    

