Python有比特字段类型吗?

时间:2008-09-27 02:47:40

标签: python bit-fields bitarray

我需要一个布尔数组的紧凑表示,Python是否有内置的位域类型,还是需要找到替代解决方案?

12 个答案:

答案 0 :(得分:30)

如果您主要希望能够为您的位字段命名并轻松操作它们,例如要使用在通信协议中表示为单个位的标志,您可以使用ctypes的标准结构和联合功能,如How Do I Properly Declare a ctype Structure + Union in Python? - Stack Overflow

中所述

例如,要单独处理一个字节的4个最低有效位,只需在LittleEndianStructure中将它们命名为最小到最重要。您使用联合提供对字节或int的相同数据的访问,以便您可以将数据移入或移出通信协议。在这种情况下,这是通过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 :(得分:28)

当我最近有类似的需求时,

Bitarray是我找到的最佳答案。它是一个C扩展(比BitVector快得多,它是纯python)并将其数据存储在一个实际的位域中(因此它的内存效率是一个numpy布尔数组的8倍,这似乎每个元素使用一个字节。)

答案 2 :(得分:12)

您应该查看最近达到2.0版的bitstring模块。 二进制数据紧凑地存储为字节数组,可以轻松创建,修改和分析。

您可以从二进制,八进制,十六进制,整数(大或小端),字符串,字节,浮点数,文件等创建BitString个对象。

a = BitString('0xed44')
b = BitString('0b11010010')
c = BitString(int=100, length=14)
d = BitString('uintle:16=55, 0b110, 0o34')
e = BitString(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] = 'uint:11=33, int:9=-1'

还有一个位位置的概念,因此如果对您有用,您可以将其视为文件或流。属性用于对位数据进行不同的解释。

w = g.read(10).uint
x, y, z = g.readlist('int:4, int:4, hex:32')
if g.peek(8) == '0x00':
    g.pos += 10

此外,还支持标准的逐位二进制运算符,打包,解包,字节序等。最新版本适用于Python 2.7和3.x,虽然它是纯Python,但在内存和速度方面进行了相当好的优化。

答案 3 :(得分:6)

我使用二进制逐位运算符!,&amp;,|,^,&gt;&gt;和&lt;&lt;。它们工作得很好,直接在底层C中实现,它通常直接在底层硬件上实现。

答案 4 :(得分:6)

将每个值表示为2的幂:

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

然后设置值为true:

table = table | testB

设置值false:

table = table & (~testC)

测试值:

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

如果这对您没有意义,请深入研究十六进制表示。这基本上就是如何跟踪嵌入式C应用程序中的布尔标志(如果你有限制内存)。

答案 5 :(得分:5)

BitVector包可能就是您所需要的。它不是内置于我的python安装中,但很容易在python站点上进行跟踪。

当前版本

https://pypi.python.org/pypi/BitVector

答案 6 :(得分:4)

NumPy有一个array interface模块,您可以用它来制作位域。

答案 7 :(得分:2)

如果您的位域很短,您可以使用the struct module。否则我会推荐某种the array module的包装器。

此外,ctypes模块确实包含bitfields,但我自己从未使用过它。 警告经纪人

答案 8 :(得分:1)

如果要使用整数(或长整数)表示为bool数组(或整数组),请查看http://sourceforge.net/projects/pybitop/files/

它将位域的插入/提取提供为长整数;找到最重要或最不重要的'1'位;计算所有的1;位反向;像纯python一样可能的东西,但在C中更快。

答案 9 :(得分:0)

对于大多数连续位,有一个https://pypi.org/project/range_set/模块,该模块与Python内置的b = [1,2,3,4,5,6,7,8,9,10,11] 兼容。顾名思义,它将位存储为开始/结束对。

答案 10 :(得分:0)

我必须在通信协议中处理一些控制字/标志,而我的重点是编辑器为我提供了标志名的建议,并跳转到带有“ F3”的标志的定义。 下面的代码满足了这些要求(很遗憾,今天的PyCharm索引器不支持@nealmcb的ctypes解决方案。 )。欢迎提出建议:

""" The following bit-manipulation methods are written to take a tuple as input, which is provided by the Bitfield class. The construct 
looks weired, however the call to a setBit() looks ok and the editor (PyCharm) suggests all 
possible bit names. I did not find a more elegant solution that calls the setBit()-function and needs 
only one argument.
Example call:
    setBit( STW1.bm01NoOff2() ) """

def setBit(TupleBitField_BitMask):
    # word = word | bit_mask
    TupleBitField_BitMask[0].word = TupleBitField_BitMask[0].word | TupleBitField_BitMask[1]


def isBit(TupleBitField_BitMask):
    # (word & bit_mask) != 0
    return (TupleBitField_BitMask[0].word & TupleBitField_BitMask[1]) !=0


def clrBit(TupleBitField_BitMask):
    #word = word & (~ BitMask)
    TupleBitField_BitMask[0].word = TupleBitField_BitMask[0].word & (~ TupleBitField_BitMask[1])


def toggleBit(TupleBitField_BitMask):
    #word = word ^ BitMask
    TupleBitField_BitMask[0].word = TupleBitField_BitMask[0].word ^ TupleBitField_BitMask[1]

""" Create a Bitfield type for each control word of the application. (e.g. 16bit length). 
Assign a name for each bit in order that the editor (e.g. PyCharm) suggests the names from outside. 
The bits are defined as methods that return the corresponding bit mask in order that the bit masks are read-only
and will not be corrupted by chance.
The return of each "bit"-function is a tuple (handle to bitfield, bit_mask) in order that they can be 
sent as arguments to the single bit manipulation functions (see above): isBit(), setBit(), clrBit(), toggleBit()
The complete word of the Bitfield is accessed from outside by xxx.word.
Examples:
    STW1 = STW1Type(0x1234) # instanciates and inits the bitfield STW1, STW1.word = 0x1234
    setBit(STW1.bm00() )    # set the bit with the name bm00(), e.g. bm00 = bitmask 0x0001
    print("STW1.word =", hex(STW1.word))
"""
class STW1Type():
    # assign names to the bit masks for each bit (these names will be suggested by PyCharm)
    #    tip: copy the application's manual description here
    def __init__(self, word):
        # word = initial value, e.g. 0x0000
        self.word = word

    # define all bits here and copy the description of each bit from the application manual. Then you can jump
    #    to this explanation with "F3"
    #    return the handle to the bitfield and the BitMask of the bit.
    def bm00NoOff1_MeansON(self):
        # 0001 0/1= ON (edge)(pulses can be enabled)
        #        0 = OFF1 (braking with ramp-function generator, then pulse suppression & ready for switching on)
        return self, 0x0001

    def bm01NoOff2(self):
        # 0002  1 = No OFF2 (enable is possible)
        #       0 = OFF2 (immediate pulse suppression and switching on inhibited)
        return self, 0x0002

    def bm02NoOff3(self):
        # 0004  1 = No OFF3 (enable possible)
        #       0 = OFF3 (braking with the OFF3 ramp p1135, then pulse suppression and switching on inhibited)
        return self, 0x0004

    def bm03EnableOperation(self):
        # 0008  1 = Enable operation (pulses can be enabled)
        #       0 = Inhibit operation (suppress pulses)
        return self, 0x0008

    def bm04RampGenEnable(self):
        # 0010  1 = Hochlaufgeber freigeben (the ramp-function generator can be enabled)
        #       0 = Inhibit ramp-function generator (set the ramp-function generator output to zero)
        return self, 0x0010

    def b05RampGenContinue(self):
        # 0020  1 = Continue ramp-function generator
        #       0 = Freeze ramp-function generator (freeze the ramp-function generator output)
        return self, 0x0020

    def b06RampGenEnable(self):
        # 0040  1 = Enable speed setpoint; Drehzahlsollwert freigeben
        #       0 = Inhibit setpoint; Drehzahlsollwert sperren (set the ramp-function generator input to zero)
        return self, 0x0040

    def b07AcknowledgeFaults(self):
        # 0080 0/1= 1. Acknowledge faults; 1. Quittieren Störung
        return self, 0x0080

    def b08Reserved(self):
        # 0100 Reserved
        return self, 0x0100

    def b09Reserved(self):
        # 0200 Reserved
        return self, 0x0200

    def b10ControlByPLC(self):
        # 0400  1 = Control by PLC; Führung durch PLC
        return self, 0x0400

    def b11SetpointInversion(self):
        # 0800  1 = Setpoint inversion; Sollwert Invertierung
        return self, 0x0800

    def b12Reserved(self):
        # 1000 Reserved
        return self, 0x1000

    def b13MotorPotiSPRaise(self):
        # 2000 1 = Motorized potentiometer setpoint raise; (Motorpotenziometer Sollwert höher)
        return self, 0x2000

    def b14MotorPotiSPLower(self):
        # 4000 1 = Motorized potentiometer setpoint lower; (Motorpotenziometer Sollwert tiefer)
        return self, 0x4000

    def b15Reserved(self):
        # 8000 Reserved
        return self, 0x8000


""" test the constrution and methods """
STW1 = STW1Type(0xffff)
print("STW1.word                =", hex(STW1.word))

clrBit(STW1.bm00NoOff1_MeansON())
print("STW1.word                =", hex(STW1.word))

STW1.word = 0x1234
print("STW1.word                =", hex(STW1.word))

setBit( STW1.bm00NoOff1_MeansON() )
print("STW1.word                =", hex(STW1.word))

clrBit( STW1.bm00NoOff1_MeansON() )
print("STW1.word                =", hex(STW1.word))

toggleBit(STW1.bm03EnableOperation())
print("STW1.word                =", hex(STW1.word))

toggleBit(STW1.bm03EnableOperation())
print("STW1.word                =", hex(STW1.word))

print("STW1.bm00ON              =", isBit(STW1.bm00NoOff1_MeansON() ) )
print("STW1.bm04                =", isBit(STW1.bm04RampGenEnable()  ) )

它打印出来:

STW1.word                = 0xffff
STW1.word                = 0xfffe
STW1.word                = 0x1234
STW1.word                = 0x1235
STW1.word                = 0x1234
STW1.word                = 0x123c
STW1.word                = 0x1234
STW1.bm00ON              = False
STW1.bm04                = True

答案 11 :(得分:0)

我需要一个没有外部依赖性的最小的内存有效位域,这里是:

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