【问题标题】:Does Python have a bitfield type?Python 有位域类型吗?
【发布时间】:2010-09-13 15:45:49
【问题描述】:

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

【问题讨论】:

标签: python bit-fields bitarray


【解决方案1】:

如果您主要希望能够命名您的位字段并轻松操作它们,例如要使用在通信协议中表示为单个位的标志,则可以使用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。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • BitArray 可以安装在 Windows 上吗?
    • 看起来BitArray 可以在 Linux 上安装,但页面上没有任何内容建议在 Windows 上安装 PIP。无赖...
    • Good old Christoph Gohlke 我说windows bitarray build :) 该网站可能会说“用于 Python 扩展包的非官方 Windows 二进制文件”,但我使用过无数包,从来没有遇到过问题。
    【解决方案3】:

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

    您可以从二进制、八进制、十六进制、整数(大端或小端)、字符串、字节、浮点数、文件等创建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,但在内存和速度方面进行了相当好的优化。

    【讨论】:

    • 我喜欢那个!对我来说比 bitarray 更直观一点。谢谢!
    【解决方案4】:

    将每个值表示为 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 应用程序中跟踪布尔标志的方式(如果您的内存有限)。

    【讨论】:

    • 很好的答案。我喜欢和不喜欢它同时是手动的。不过,没有更合​​乎逻辑的方式来手动构造位域类。
    【解决方案5】:

    我使用二进制位运算符 !、&、|、^、>> 和

    【讨论】:

      【解决方案6】:

      BitVector 包可能是您需要的。它没有内置到我的 python 安装中,但很容易在 python 站点上找到。

      https://pypi.python.org/pypi/BitVector 为当前版本。

      【讨论】:

        【解决方案7】:

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

        【讨论】:

        • 内置的 array 模块对于位数组也足够了,并且比 NumPy 更便携(跨 Python 实现)。
        【解决方案8】:

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

        此外,ctypes 模块确实包含bitfields,但我自己从未使用过它。 告诫购买者

        【讨论】:

        • 但似乎 struct 模块将每个位表示为一个字符或字节,因此它并没有像通常定义的那样真正处理位字段(位在内存中紧密地打包在一起)。
        【解决方案9】:

        如果您想使用整数(或长整数)表示为布尔数组(或整数集),请查看http://sourceforge.net/projects/pybitop/files/

        它提供将位域插入/提取到长整数;找到最重要或最不重要的“1”位;计算所有的 1;位反转;类似的东西在纯 python 中都是可能的,但在 C 中要快得多。

        【讨论】:

          【解决方案10】:

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

          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
          

          【讨论】:

            【解决方案11】:

            对于大部分连续位,有 https://pypi.org/project/range_set/ 模块,它与 Python 的内置 set 兼容。顾名思义,它将位存储为开始/结束对。

            【讨论】:

              【解决方案12】:

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

              """ 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
              

              【讨论】:

                猜你喜欢
                • 2018-09-29
                • 2019-09-12
                • 2016-02-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-10-17
                • 2017-03-04
                • 1970-01-01
                相关资源
                最近更新 更多