【问题标题】:How to utilize every bit of a bytearray in python如何在python中利用字节数组的每一位
【发布时间】:2021-08-11 18:01:53
【问题描述】:

我有一个这样的字节数组:

a=bytearray([False]*size)

我想将False 值存储为字节数组而不是字节;我在乎记忆。反正我能做到吗?因为使用 8 位存储 1 位值似乎是一种浪费。我也不能使用位数组导入。我现在已经花了几个小时在这方面,所以任何帮助都将不胜感激。

【问题讨论】:

  • 字节数组的每个元素都是 0 - 255 (0x00 - 0xff) 范围内的整数。因此,您可以为每个元素打包 8 位。您可能必须自己管理索引 - 只需将位索引右移 3 即可获得字节索引,并使用移位和位掩码来访问所需的位。
  • 你能告诉我一个如何做到这一点的例子吗?因为我想将字节数组中的每一位都设置为假,然后根据一些哈希值我将它们设置为真。所以你说的回答了我的问题,但你能告诉我如何在代码中做到这一点,所以我不会浪费太多内存吗?
  • 如果你真的这么关心内存使用,你应该使用比bytearray更好的数据结构(例如来自this question的东西)。但是,请记住“过早的优化是万恶之源”。你真的需要优化这个吗?
  • 如果“标准python库”中有比bytearray更好的数据结构可以做我上面解释的事情,请告诉我。
  • stdlib 没有像这样的内置 AFAIK 的东西,因为这种情况非常罕见。如果你真的需要这个,你应该使用一个专门的库或者编写你自己的类。

标签: python arrays byte bit bitarray


【解决方案1】:

这是一个简单的类,可以做你想做的事。它在bytearray 类型之上实现位数组:

class BitArray:
    def __init__(self, size):
        self.size = size
        self.bytearray = bytearray((size + 7) >> 3)

    def clear(self):
        ba = self.bytearray
        for i in range(len(ba)):
            ba[i] = 0

    def get_bit(self, bit_ix):
        if not isinstance(bit_ix, int):
            raise IndexError("bit array index not an int")

        if bit_ix < 0 or bit_ix >= self.size:
            raise IndexError("bit array index out of range")

        byte_ix = bit_ix >> 3
        bit_ix = bit_ix & 7
        return (self.bytearray[byte_ix] >> bit_ix) & 1

    def set_bit(self, bit_ix, val):
        if not isinstance(bit_ix, int):
            raise IndexError("bit array index not an int")

        if bit_ix < 0 or bit_ix >= self.size:
            raise IndexError("bit array index out of range")

        if not isinstance(val, int):
            raise ValueError("bit array value not an int")

        if val not in (0, 1):
            raise ValueError("bit array value must be 0 or 1")

        byte_ix = bit_ix >> 3
        bit_ix = bit_ix & 7
        bit_val = 1 << bit_ix

        if val:
            self.bytearray[byte_ix] |= bit_val
        else:
            self.bytearray[byte_ix] &= ~bit_val

    def __getitem__(self, key):
        return self.get_bit(key)

    def __setitem__(self, key, value):
        self.set_bit(key, value)

位数组在创建时最初全为零。 clear 方法将所有位设置为零,get_bit 方法将返回位的值,set_bit 方法将设置位的值。所有位值都是01(尽管您可以轻松地将其更改为TrueFalse)。

这是一个如何使用它的示例:

ba = BitArray(23)
print(ba.get_bit(17))

ba.set_bit(17, 1)
print(ba.get_bit(17))

ba.clear()
print(ba.get_bit(17))

这将创建一个足以容纳 23 位的位数组。然后它打印第 17 位,即 0。然后将第 17 位设置为 1 并打印它。然后它清除整个数组并再次打印第 17 位。输出为:

0
1
0

更新:我添加了__getitem____setitem__,因此您现在可以使用索引符号直接索引各个位。示例:

ba[17] = 1
print(ba[17])

这会将第 17 位设置为 1,然后打印它。输出是:

1

请注意,索引必须是整数。不支持切片。

【讨论】:

    猜你喜欢
    • 2015-05-09
    • 1970-01-01
    • 2017-10-02
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多