Есть ли встроенный набор битов, который похож на std::bitset из C++?

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

#include<bitset>
int main() {
    std::bitset<100> numBits;
}

Однако я не знаю, есть ли что-то подобное в Python, желательно встроенное.

4 ответа

Решение

Для этого нет ничего встроенного. Если вам нужна такая структура данных для правильного вывода байтов с правильными установленными битами, например, для сетевого протокола, двоичной файловой структуры или аппаратного контроля, последовательность списка значений True и False с последовательностью Байт легко выполним.

Можно также создать класс, чтобы разрешить непосредственное множество битов в памяти в объекте bytearray. Однако вряд ли то, что происходит в C++, не даст вам преимуществ в скорости или памяти (хорошо, для больших наборов битов вы можете получить память) - Python будет обрабатывать каждый бит как полную ссылку на объекты True или False (или на полные 0 и 1 целые числа) независимо от того, что вы делаете в коде.

Тем не менее, если у вас есть список со значениями True и False, которые вы хотите вывести, скажем, в файл в виде последовательности битов, такой код может работать:

a = [True, True, False, False, False, True, ...]
with open("myfile.bin", "wb" as file):
    for i, value in enumerate(a):
        if not i % 8:
            if i:
                file.write(byte)
            byte = 0
        byte <<= 1
        byte |= value
     if i % 8:
        byte <<= (8 - i % 8)
        file.write(byte)

Более сложный способ - создать для него полную поддержку класса, сохраняя значения в объекте bytearray и вычисляя каждый битовый индекс при операциях set и reset - минимальный способ сделать это:

class BitArray(object):
    def __init__(self, lenght):
        self.values = bytearray(b"\x00" * (lenght // 8 + (1 if lenght % 8  else 0)))
        self.lenght = lenght

    def __setitem__(self, index, value):
        value = int(bool(value)) << (7 - index % 8)
        mask = 0xff ^ (7 - index % 8)
        self.values[index // 8] &= mask
        self.values[index // 8] |= value
    def __getitem__(self, index):
        mask = 1 << (7 - index % 8)
        return bool(self.values[index // 8] & mask)

    def __len__(self):
        return self.lenght

    def __repr__(self):
        return "<{}>".format(", ".join("{:d}".format(value) for value in self))

Как вы можете видеть, при этом нет прироста скорости, и вам понадобится много битов, чтобы воспользоваться преимуществами экономии памяти. Это пример использования приведенного выше класса в интерактивном режиме:

In [50]: a = BitArray(16)

In [51]: a[0] = 1

In [52]: a[15] = 1

In [53]: a
Out[53]: <1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>

Обычно вы просто встроенный int класс (или long в python2). Может быть, заверните это в классе, если вам особенно важно скрыть смены.

Ну, вы могли бы сделать "bitset", используя список логических значений:

mybitset = [True, False, False, True, False]

Мне не хватает контекста, чтобы дать вам более подходящий ответ.

Вы могли бы использовать обычный list; однако это не очень эффективно для памяти: в 32-битных сборках Python он будет тратить 4 байта на "бит", а в 64-битных сборках - 8 байтов. Это потому, что элементы списка на самом деле (ссылки) на другие объекты Python.

Стандартная библиотека Python также имеет встроенный array модуль, который гораздо более эффективен для хранения однородных значений, чем общий list, но, к сожалению, он не поддерживает биты в качестве типа данных. Кроме того, он не обеспечивает Set интерфейс.

Таким образом, если эффективность использования памяти вызывает беспокойство, то ваш выбор сводится к тому, чтобы либо построить собственную реализацию набора битов Python поверх array или установка стороннего модуля из PyPI, такой как intbitset

Другие вопросы по тегам