Python 是否有位字段类型?

57

我需要一个布尔数组的紧凑表示方式,Python中是否有内置的位域类型或者我需要寻找一种替代方案?


如果术语含义不明确,我理解您想要的是C位域中提供的功能,或者在这里描述的功能?http://en.wikipedia.org/wiki/Bit_field - nealmcb
12个回答

50
如果你主要想给位域命名并且轻松操作它们,例如处理在通信协议中表示为单个位的标志,则可以使用ctypes的标准结构和联合功能,如How Do I Properly Declare a ctype Structure + Union in Python? - Stack Overflow所述。
例如,要单独处理字节的最低有效位的4位,只需按从最低到最高的顺序在LittleEndianStructure中命名它们。 你可以使用联合来提供对与字节或整数相同数据的访问,以便将数据移入或移出通信协议。 在这种情况下,通过flags.asbyte字段完成。
import ctypes
c_uint8 = ctypes.c_uint8

class Flags_bits(ctypes.LittleEndianStructure):
    _fields_ = [
            ("logout", c_uint8, 1),
            ("userswitch", c_uint8, 1),
            ("suspend", c_uint8, 1),
            ("idle", c_uint8, 1),
        ]

class Flags(ctypes.Union):
    _fields_ = [("b", Flags_bits),
                ("asbyte", c_uint8)]

flags = Flags()
flags.asbyte = 0xc

print(flags.b.idle)
print(flags.b.suspend)
print(flags.b.userswitch)
print(flags.b.logout)

这四个比特位(我在此打印,从最重要的开始,这样在打印时更自然)是1、1、0、0,即二进制的0xc。

1
结构体的打包有时可能会导致意外结果: https://github.com/python/cpython/pull/19850#issuecomment-869410686 - Jaakko

32

Bitarray是我最近寻找类似需求时找到的最佳解决方案。它是一个C扩展程序(比纯python的BitVector快得多),并且将其数据存储在实际的位域中(因此它比numpy布尔数组更节省8倍的内存,后者似乎每个元素使用一个字节)。


1
BitArray 在 Windows 上可安装吗? - IAbstract
1
看起来 BitArray 在 Linux 上可以轻松安装,但页面上没有任何关于 Windows 的 PIP 安装的提示。有点遗憾... - IAbstract
我说,好老的Christoph Gohlke windows bitarray build :) 这个网站可能会说“Python扩展包的非官方Windows二进制文件”,但我已经使用了很多软件包,从未遇到过任何问题。 - Mark Lawrence

16
您应该看一下最近发布的版本为4.0的bitstring模块。 二进制数据被紧凑地存储为字节数组,可以轻松创建、修改和分析。
您可以从二进制、八进制、十六进制、整数(大端或小端)、字符串、字节、浮点数、文件等创建bitstring对象。
from bitstring import BitArray, BitStream
a = BitArray('0xed44')
b = BitArray('0b11010010')
c = BitArray(int=100, length=14)
d = BitArray('uintle:16=55, 0b110, 0o34')
e = BitArray(bytes='hello')
f = pack('<2H, bin:3', 5, 17, '001') 

您可以使用简单的函数或切片表示法进行分析和修改,而无需担心位掩码等问题。
a.prepend('0b110')
if '0b11' in b:
    c.reverse()
g = a.join([b, d, e])
g.replace('0b101', '0x3400ee1')
if g[14]:
    del g[14:17]
else:
    g[55:58] = 'uint11=33, int9=-1'

还有一个位位置的概念,这样你就可以像处理文件或流一样使用它,如果这对你有用的话。属性用于给出位数据的不同解释。

g = BitStream(g)
w = g.read(10).uint
x, y, z = g.readlist('int4, int4, hex32')
if g.peek(8) == '0x00':
    g.pos += 10

此外,还支持标准的位运算二进制操作符、打包、解包、字节序等功能。最新版本适用于Python 3.7及更高版本,并在内存和速度方面进行了合理的优化。

1
我喜欢那个!对我来说比bitarray更直观一些。谢谢! - weronika
你应该公开自己是bitstring模块的作者。 - undefined
@shrewmouse:嗯,我并没有刻意隐藏这个事实——我在S.O.上使用的是真实姓名,并在其他很多答案中提到了这是我的模块,当然,在模块本身和文档中也有提及,此外,我的S.O.个人简介中也有相关信息,这意味着在这个答案中,我的个人资料图片的工具提示中也有相关信息。 - undefined

10

将您的每个值表示为2的幂:

testA = 2**0
testB = 2**1
testC = 2**3

然后设置值为真:

table = table | testB

设置一个值为false:

table = table & (~testC)

测试某个值:

bitfield_length = 0xff
if ((table & testB & bitfield_length) != 0):
    print "Field B set"

如果您对十六进制表示法感到困惑,请深入了解一下。这基本上是在嵌入式C应用程序中跟踪布尔标志的方法(如果您的内存有限)。

很棒的答案。我同时喜欢和不喜欢它是手动的。虽然没有更合适的方式手动构建一个 bitfield 类。 - RobotHumans

7

我使用二进制位运算符!、&、|、^、>>和<<。它们工作得非常好,直接在底层的C语言中实现,通常直接在底层硬件上实现。


5

4

NumPy有一个数组接口模块,您可以使用它来创建位域。


内置的array模块也足以处理位数组,并且比NumPy更具可移植性(跨Python实现)。 - gsnedders

2
如果你的位域很短,你可以使用struct模块。否则,我建议在array模块周围包装一些东西。
此外,ctypes模块确实包含位域,但我自己从未使用过。请注意风险。

1
但是似乎struct模块将每个位表示为char或byte,因此它实际上并不处理通常定义的位域(其中位在内存中紧密打包在一起)。 - nealmcb

1

如果你想使用整数(或长整数)表示布尔数组(或整数集合),可以看一下http://sourceforge.net/projects/pybitop/files/

它提供了将位域插入/提取到长整数中;查找最高位或最低位的“1”位;计算所有“1”的数量;位反转等功能,这些在纯Python中都是可能的,但在C中速度更快。


1

我需要一个没有外部依赖的最小化、内存高效的位域,这就是它:

import math

class Bitfield:
    def __init__(self, size):
        self.bytes = bytearray(math.ceil(size / 8))

    def __getitem__(self, idx):
        return self.bytes[idx // 8] >> (idx % 8) & 1

    def __setitem__(self, idx, value):
        mask = 1 << (idx % 8)
        if value:
            self.bytes[idx // 8] |= mask
        else:
            self.bytes[idx // 8] &= ~mask

使用:

# if size is not a multiple of 8, actual size will be the next multiple of 8
bf = Bitfield(1000)
bf[432] # 0
bf[432] = 1
bf[432] # 1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接