Есть ли в Python тип битового поля?

Мне нужно компактное представление массива логических значений, есть ли у Python встроенный тип битового поля или мне нужно будет найти альтернативное решение?

Для случаев, когда термин неоднозначен, я так понимаю, что вам нужны виды функций, доступные в битовых полях C, или как описано здесь? en.wikipedia.org/wiki/Bit_field

nealmcb 14.07.2012 07:55
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
54
1
52 270
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

NumPy имеет модуль интерфейс массива, который вы можете использовать для создания битового поля.

Встроенного модуля множество достаточно и для битового массива, и он более переносим (через Python), чем NumPy.

gsnedders 18.04.2012 19:52

Пакет BitVector может быть тем, что вам нужно. Он не встроен в мою установку Python, но его легко отследить на сайте Python.

https://pypi.python.org/pypi/BitVector для текущей версии.

Ответ принят как подходящий

Bitarray был лучшим ответом, который я нашел, когда недавно у меня была аналогичная потребность. Это расширение C (намного быстрее, чем BitVector, который является чистым питоном) и хранит свои данные в фактическом битовом поле (так что он в восемь раз эффективнее памяти, чем логический массив numpy, который, похоже, использует байт на элемент).

BitArray доступен для установки в Windows?

IAbstract 06.02.2016 22:19

Похоже, что BitArray легко доступен для установки в Linux, но ничто на странице не предлагает установку PIP для Windows. Облом ...

IAbstract 08.02.2016 20:15

Старый добрый Кристоф Гольке, я говорю сборка битового массива Windows :) На сайте может быть написано «Неофициальные двоичные файлы Windows для пакетов расширений Python», но я использовал множество пакетов и ни разу не столкнулся с проблемой.

Mark Lawrence 28.01.2021 17:11

Если ваше битовое поле короткое, вы, вероятно, можете использовать структурный модуль. В противном случае я бы рекомендовал какую-то оболочку вокруг модуль массива.

Кроме того, модуль ctypes действительно содержит битовые поля, но сам я его никогда не использовал. Пусть покупатель будет бдителен.

Но кажется, что модуль структуры представляет каждый бит как символ или байт, поэтому он на самом деле не обрабатывает битовые поля, как обычно (где биты плотно упакованы в памяти).

nealmcb 14.07.2012 07:39

Я использую двоичные побитовые операторы!, &, |, ^, >> и <<. Они работают очень хорошо и реализуются непосредственно в базовом C, который обычно находится непосредственно на базовом оборудовании.

Представьте каждую из своих ценностей как степень двойки:

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 (если у вас ограниченная память).

Отличный ответ. Мне нравится и не нравится, что это руководство одновременно. Однако нет более логичного способа вручную создать класс битового поля.

RobotHumans 17.09.2013 00:59

Вам следует взглянуть на модуль битовая строка, который недавно достиг версии 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. Спасибо!

weronika 22.06.2011 03:26

Если вы хотите использовать целые числа (или длинные целые числа) для представления в виде массивов логических значений (или наборов целых чисел), взгляните на http://sourceforge.net/projects/pybitop/files/

Он обеспечивает вставку / извлечение битовых полей в длинные целые числа; поиск наиболее значимого или наименее значимого бита «1»; подсчет всех единиц; бит-инверсия; подобные вещи, которые возможны в чистом питоне, но намного быстрее в C.

Если вы в основном хотите иметь возможность давать имена своим битовым полям и легко ими манипулировать, например для работы с флагами, представленными как отдельные биты в протоколе связи, вы можете использовать стандартные функции структуры и объединения ctypes, как описано в Как правильно объявить структуру ctype + Union в Python? - Переполнение стека

Например, чтобы работать с 4 наименее значимыми битами байта по отдельности, просто назовите их от наименее до наиболее значимого в LittleEndianStructure. Вы используете объединение, чтобы предоставить доступ к тем же данным, что и byte или 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 в двоичном формате.

Для в основном последовательных битов есть модуль https://pypi.org/project/range_set/, который API-интерфейс совместим со встроенным в Python set. Как следует из названия, он хранит биты как пары начало / конец.

Мне пришлось иметь дело с некоторыми контрольными словами / флагами в протоколе связи, и я сосредоточился на том, чтобы редактор предлагал мне имена флагов и переходил к определению флагов с помощью «F3». Приведенный ниже код удовлетворяет этим требованиям (решение с ctypes от @nealmcb, к сожалению, сегодня не поддерживается индексатором 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

Мне нужно минимальное битовое поле с эффективным использованием памяти без внешних зависимостей, вот оно:

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

Другие вопросы по теме