Packing 4 Integers as ONE BYTE?

Use shift and bitwise OR, then convert to a character to get a "byte":

x = chr(a | (b << 1) | (c << 2) | (d << 5))

To unpack this byte again, first convert to an integer, then shift and use bitwise AND:

i = ord(x)
a = i & 1
b = (i >> 1) & 1
c = (i >> 2) & 7
d = (i >> 5) & 7

Explanation: Initially, you have

0000000a
0000000b
00000ccc
00000ddd

The left-shifts give you

0000000a
000000b0
000ccc00
ddd00000

The bitwise OR results in

dddcccba

Converting to a character will convert this to a single byte.

Unpacking: The four different right-shifts result in

dddcccba
0dddcccb
00dddccc
00000ddd

Masking (bitwise AND) with 1 (0b00000001) or 7 (0b00000111) results in

0000000a
0000000b
00000ccc
00000ddd

again.


def encode(a, b, c, d):
  return a | b << 1 | c << 2 | d << 5

def decode(x):
  return x & 1, (x >> 1) & 1, (x >> 2) & 7, (x >> 5) & 7

If you need to this kind of thing a lot then bit shifting can become tedious and error prone. There are third-party libraries that can help - I wrote one called bitstring:

To pack and convert to a byte:

x = bitstring.pack('2*uint:1, 2*uint:3', a, b, c, d).bytes

and to unpack:

a, b, c, d = bitstring.BitArray(bytes=x).unpack('2*uint:1, 2*uint:3')

This is probably overkill for your example, but it's helpful when things get more complicated.


Pretty simple. Mask (for range), shift them into place, and or them together.

packed = ((a & 1) << 7) | ((b & 1) << 6) | ((c & 7) << 3) | (d & 7)

a = (packed >> 7) & 1
b = (packed >> 6) & 1
c = (packed >> 3) & 7
d = packed & 7