python: преобразовать строку бит ('0', '1') в массив знаковых 32битных чисел

Есть срока бит: '01010101...'

Подскажите как поэлегантнее преобразовать ее в массив 32битных знаковых чисел?

Решение в лоб работает, но оно некрасивое :)

nums = []
for i in range(0, len(bits), 32):
    value = int(bits[i:i + 32], 2)
    nums.append(value if value <= 0x7fffffff else (value - 4294967296))

return nums 

Что-нибудь из арсенала numpy или struct + bitarray


Ответы (3 шт):

Автор решения: insolor

Если на "голом" Python, то я бы сделал в два отдельных этапа: преобразование битов в байты, потом преобразование байт в знаковые инты. Получится длиннее, но без вычитаний магических чисел:

def bits_to_bytes(bits: str):
    for i in range(0, len(bits), 8):
        yield int(bits[i : i + 8], 2)


def bytes_to_ints(b: bytes):
    for i in range(0, len(b), 4):
        yield int.from_bytes(b[i : i + 4], "big", signed=True)


import random

n = 10  # В итоге должно получиться 10 чисел
bits = "".join(random.choices("01", k=8 * 4 * n))
print(bits)

bts = bytes(bits_to_bytes(bits))
print(bts)

ints = list(bytes_to_ints(bts))
print(ints)

Пример вывода:

10010011000101110011111000111100100110111110011001001001110111100110110001101000011010011011101011001011011110110010101010000110110001010000000010100100011010111101110101100111100110100011111001111010101110110011111100011100000110000011001110111000010010011111010011101010110101110110100011000000001101111000010110100101
b'\x93\x17><\x9b\xe6I\xdelhi\xba\xcb{*\x86\xc5\x00\xa4k\xddg\x9a>z\xbb?\x1c\x183\xb8I\xf4\xea\xd7h\xc07\x85\xa5'
[-1827193284, -1679406626, 1818782138, -881120634, -989813653, -580412866, 2059091740, 406042697, -185936024, -1070103131]
→ Ссылка
Автор решения: extrn

как вариант

def f(x):
    return np.packbits(np.fromiter(x, dtype='b'), bitorder='big').view(dtype='>i4')
>>> x = f'{-987654321 & 0xffffffff:032b}{1234567890:032b}'
>>> x
'1100010100100001100101110100111101001001100101100000001011010010'
>>> f(x)
array([-987654321, 1234567890], dtype=int32)
→ Ссылка
Автор решения: Stanislav Volodarskiy

Голый питон без циклов. Строка переводится в целое число, целое число в массив байт, массив байт в массив четырёхбайтовых целых, массив четырёхбайтовыx целых в массив целых. По пути порядок значений перевернулся:

import array


def bits_to_ints(bits):
    assert len(bits) % 32 == 0
    i = int(bits, 2)
    b = i.to_bytes(len(bits) // 8, 'little', signed=False)
    a = array.array('i')
    a.frombytes(b)
    return list(a[::-1])


bits = (
    '10000000000000000000000000000000' # -2147483648
    '11111111111111111111111111110110' #         -10
    '11111111111111111111111111111111' #          -1
    '00000000000000000000000000000000' #           0
    '00000000000000000000000000000010' #           2
    '00000000000000000000000000010100' #          20
    '01111111111111111111111111111111' #  2147483647
)

print(bits_to_ints(bits))
$ python restore_ints.py
[-2147483648, -10, -1, 0, 2, 20, 2147483647]

Алгоритм линейный:

 число   время
 бит     работы (c)

 10^5    0.0003
 10^6    0.0030
 10^7    0.0291
 10^8    0.2974
 10^9    2.9931

P.S. Это извращение. Если вы понимаете почему в конце нужно перевернуть массив - вы молодец.

P.P.S Последнее преобразование в list можно не делать. Целые в array такие же целые как и любые другие.

→ Ссылка