web-dev-qa-db-de.com

Hat Python einen Bitfield-Typ?

Ich brauche eine kompakte Darstellung eines Arrays von Booleans. Hat Python einen eingebauten Bitfield-Typ oder muss ich eine alternative Lösung finden?

42
Gordon Wrigley

Bitarray war die beste Antwort, die ich gefunden habe, als ich kürzlich ein ähnliches Bedürfnis hatte. Es ist eine C-Erweiterung (so viel schneller als BitVector, was reines Python ist) und speichert seine Daten in einem tatsächlichen Bitfeld (also achtmal speichereffizienter als ein numpy-boolesches Array, das ein Byte pro Element zu verwenden scheint).

28
Alex Coventry

Wenn Sie hauptsächlich Ihre Bitfelder benennen und sie leicht bearbeiten können, z. Um mit Flags zu arbeiten, die als einzelne Bits in einem Kommunikationsprotokoll dargestellt werden, können Sie die standardmäßigen Struktur- und Union-Funktionen von ctypes verwenden, wie unter beschrieben. Wie deklariere ich eine ctype-Struktur + Union in Python? - Paketüberfluss

Um beispielsweise mit den 4 niedrigstwertigen Bits eines Bytes einzeln zu arbeiten, benennen Sie sie einfach in einer LittleEndianStructure von den niedrigsten bis zu den höchstwertigen Bits. Sie verwenden eine Union, um Zugriff auf dieselben Daten wie ein Byte oder ein Int zu gewähren, damit Sie die Daten in das Kommunikationsprotokoll verschieben oder aus diesem entfernen können. In diesem Fall erfolgt dies über das Feld 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)

Die vier Bits (die ich hier gedruckt habe, beginnend mit den wichtigsten, was beim Drucken natürlicher erscheint) sind 1, 1, 0, 0, d. H. 0xc in binär.

30
nealmcb

Sie sollten einen Blick auf das Modul bitstring werfen, das vor kurzem die Version 2.0 ..__ erreicht hat. Die Binärdaten werden kompakt als Byte-Array gespeichert und können einfach erstellt, geändert und analysiert werden.

Sie können BitString-Objekte aus Binär-, Oktal-, Hex-, Ganzzahlen (Big oder Little Endian), Strings, Bytes, Floats, Dateien usw. erstellen.

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') 

Sie können sie dann mit einfachen Funktionen oder Slice-Notation analysieren und ändern - Sie müssen sich nicht um Bitmasken usw. kümmern.

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'

Es gibt auch ein Konzept für eine Bitposition, sodass Sie es wie eine Datei oder einen Stream behandeln können, wenn dies für Sie nützlich ist. Eigenschaften werden verwendet, um unterschiedliche Interpretationen der Bitdaten zu erhalten.

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

Außerdem werden die standardmäßigen bitweisen Binäroperatoren unterstützt: Packen, Entpacken, Endianness und mehr. Die neueste Version ist für Python 2.7 und 3.x, und obwohl es sich um reines Python handelt, ist es hinsichtlich Speicher und Geschwindigkeit einigermaßen gut optimiert.

12
Scott Griffiths

Stellen Sie jeden Ihrer Werte als Zweierpotenz dar:

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

Dann einen Wert auf true setzen:

table = table | testB

So setzen Sie einen Wert auf "false":

table = table & (~testC)

So testen Sie einen Wert:

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

Sehen Sie sich die hexadezimale Darstellung etwas tiefer an, wenn dies für Sie nicht sinnvoll ist. Auf diese Weise behalten Sie Ihre booleschen Flags auch in einer eingebetteten C-Anwendung im Auge (wenn Sie nur begrenzten Speicher haben).

6
MattG

Ich benutze die binären bitweisen Operatoren!, &, |, ^, >> und <<. Sie funktionieren sehr gut und werden direkt in dem zugrunde liegenden C implementiert, das sich normalerweise direkt auf der zugrunde liegenden Hardware befindet.

6
S.Lott

Das BitVector-Paket könnte das sein, was Sie brauchen. Es ist nicht in meine Python-Installation integriert, aber auf der Python-Site leicht zu finden.

https://pypi.python.org/pypi/BitVector für die aktuelle Version.

5
JasonTrue

NumPy verfügt über ein array interface -Modul, mit dem Sie ein Bitfeld erstellen können.

4
Jason Baker

Wenn Ihr Bitfeld kurz ist, können Sie wahrscheinlich das struct-Modul verwenden. Ansonsten würde ich eine Art Wrapper um das Array-Modul empfehlen.

Das ctypes-Modul enthält zwar bitfields , aber ich habe es selbst nie verwendet. Caveat-Emptor.

2
Antti Rasinen

Wenn Sie Ints (oder Long Ints) verwenden möchten, um als Arrays von Boolern (oder als Mengen von Ganzzahlen) zu repräsentieren, lesen Sie http://sourceforge.net/projects/pybitop/files/ .

Es ermöglicht das Einfügen/Extrahieren von Bitfeldern in langen Ints. Finden des höchstwertigen oder niedrigstwertigen '1'-Bits; Zählen alle 1er; Bitumkehrung; Zeug wie das, was in reinem Python alles möglich ist, aber viel schneller in C.

1
greggo

Ich musste mich mit einigen Steuerwörtern/Flags in einem Kommunikationsprotokoll auseinandersetzen und mein Fokus lag darauf, dass der Editor mir Vorschläge zu den Flagsnamen gibt und mit "F3" zur Definition der Flags springt. Der folgende Code genügt diesen Anforderungen (Die Lösung mit ctypes von @nealmcb wird vom PyCharm-Indexer derzeit leider nicht unterstützt.) Vorschläge willkommen:

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

Es druckt aus:

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
0
papa

Für meist aufeinanderfolgende Bits gibt es das Modul https://pypi.org/project/range_set/ , das mit der in Python integrierten set-API kompatibel ist. Wie der Name schon sagt, speichert er die Bits als Anfang/Ende-Paare.

0