Генерирующего объекта ByteArray, выбрав эксклюзивный набор параметров


Я новичок в Python, так что советы в любой части кода приветствуются.

Моя главная проблема заключается в том, как "перегрузка" правильно функционировать. Это должно быть сделано с помощью параметров, которые передаются exculsivly? Предполагается, что только байта, слова или двойного слова передаются в соответствующий параметр. (Если прошло несколько являются самым длинным является предпочтительным. Заметка на полях: об именах базируется на старый процессор 16-битный, поэтому слово 16 бит и dword-это 32)

Я также хотел, чтобы проверить ряд дополнительных аргументов в начале, но как я по умолчанию их нет, мне пришлось проверить на нет, в первую очередь, прежде чем я смогу проверить диапазон и это сделало бы ни одно проверить лишним.

У меня было что-то вроде max(Word1,Word2) >= 2**16 в виду, потому что я надеялся, что в Python Макс даст никто и больше будет давать ложные, но > не применяется для ничего. Итак, чтобы заставить его работать я проверять не первая. Но это выглядит не элегантно.

После этого я присваиваю больше datachunks в байты и вызывать ошибки, если данные не вписываются в диапазон типа. Если ничего не передается данных имеет значение 0, чтобы заполнить массив в конец.

Если длина была дана длина рассчитывается путем проверки используется.

В итоге все данные упаковывается в объект ByteArray (сообщение, которое будет отправлено позже). Потому что объект ByteArray() проверяет, если элементы располагаются в ряд, байты не проверяются отдельно.

Есть ли более подходящие для Python способ сделать это?

def packMessage(DeviceID, Offset, dataLength=None,
                Byte0=None, Byte1=None, Byte2=None, Byte3=None,
                Word0=None, Word1=None, DWord0=None):

    Bytes = [Byte0, Byte1, Byte2, Byte3]
    length = 0
    # check if the values to be used are in range
    if Offset >= 2**16:
        raise ValueError
    else:
        Offset_low = Offset & 0x00FF
        Offset_high = (Offset & 0xFF00) >> 8

    if dataLength is not None and not 1 <= dataLength <= 4:
        raise ValueError

    # split bigger data to bytes and check the data length
    if DWord0 is not None:
        if 0 <= DWord0 <= 2**32:
            Bytes[0] = DWord0 & 0xFF
            Bytes[1] = (DWord0 & 0xFF00) >> 8
            Bytes[2] = (DWord0 & 0xFF0000) >> 16
            Bytes[3] = (DWord0 & 0xFF000000) >> 24
            length = 4
        else:
            raise ValueError
    elif Word0 is not None:
        if 0 <= Word0 <= 2**16:
            Bytes[0] = Word0 & 0xFF
            Bytes[1] = (Word0 & 0xFF00) >> 8
        else:
            raise ValueError
        if Word1 is not None:
            if 0 <= Word1 <= 2**16:
                Bytes[2] = Word1 & 0xFF
                Bytes[3] = (Word1 & 0xFF00) >> 8
                length = 4
            else:
                raise ValueError
        else:
            Word1 = 0
            Bytes[2] = Bytes[3] = 0
            length = 2
    else:
        for i in range(len(Bytes)):
            if Bytes[i] is not None:
                length = i
            else:
                Bytes[i] = 0

    # if no length given take calculated length
    if dataLength is None:
        dataLength = length

    # return the Message
    return bytearray([DeviceID, dataLength, Offset_low,
                      Offset_high, Bytes[0], Bytes[1], Bytes[2], Bytes[3]])

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

Это выглядело, как это раньше:

if Byte0 is not None:
    if Byte1 is not None:
        if Byte2 is not None:
            if Byte3 is not None:
                length = 4
            else:
                Byte3 = 0
        else:
            length = 3
            Byte2 = 0
            Byte3 = 0
    else:
        length = 2
        Byte1 = 0
        Byte2 = 0
        Byte3 = 0
else:
    length = 1
    Byte0 = 0
    Byte1 = 0
    Byte2 = 0
    Byte3 = 0

Я не вижу хороший способ, чтобы обернуть это. Так что если есть красивое решение для этого, дайте мне знать, в противном случае, не стесняйтесь игнорировать его.



170
5
задан 19 марта 2018 в 04:03 Источник Поделиться
Комментарии
1 ответ

Простой будет отдельный разбор байты, слова и слова, в своих собственных способ, это позволит упростить основную логику много

def pack_message2(DeviceID, Offset, dataLength=None,
Byte0=None, Byte1=None, Byte2=None, Byte3=None,
Word0=None, Word1=None, DWord0=None):

Offset_low, Offset_high = parse_offset(Offset)

if dataLength is not None and not 1 <= dataLength <= 4:
raise ValueError('dataLength should be between 1 and 4')

result = parse_dword(DWord0)
if result == ():
# print('no dwords')
result = tuple(parse_words(Word0, Word1))
if result == ():
# print('no words')
result = parse_bytes(Byte0, Byte1, Byte2, Byte3)

if dataLength is None:
dataLength = len(result)

result = pad_tuple_to(result, 4, 0) # if result should be padded with 0's'
# print(result, dataLength)
return bytearray([DeviceID, dataLength, Offset_low, Offset_high, *result])

Даже если вы не знаете, что реализация каждого из этих методов были, кода будет ясно, что происходит

смещение

анализируя смещение довольно просто

def parse_offset(Offset):
if Offset >= 2**16:
raise ValueError('Offset should be less than 2**16')
return (Offset & 0x00FF), ((Offset & 0xFF00) >> 8)

Данные

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

def parse_dword(dword):
# print('dword:', dword)
if dword is None:
return ()
if not 0 <= dword <= 2**32:
raise ValueError('dword should be between 0 and 2**32')
return tuple((dword & 0xFF * (2**(8*i))) >> (8 * i) for i in range(4))

def parse_words(*words):
# print('words: ', words)
for word in words:
if word is None:
# yield from (0, 0)
break
if not 0 <= word <= 2**16:
raise ValueError('word should be between 0 and 2**32')
yield from ((word & 0xFF * 2 ** (8*i)) >> (8 * i) for i in range(2))

from itertools import takewhile
def parse_bytes(*bytes_):
# print('bytes: ', bytes_)
return tuple(takewhile(lambda x: x is not None, bytes_))

Эти методы могут быть далее обобщены, но я оставлю это открытие в качестве упражнения

обивка результат

самый простой (но не чисто) способ тампонной кортеж

def pad_tuple_to(tup, to, default=None):
return tup + tuple(default for _ in range(to - len(tup)))

обобщение

Я был заинтригован обобщения, поэтому я попробовал его сам

def generalized_parse_word(size, words):
# print('words: ', words)
if words is None:
return 'empty'
for word in words:
# print('word: ', word)
if word is None:
# yield from (0, 0)
break
if not 0 <= word <= 2 ** (8 * size):
raise ValueError('word with %i should be between 0 and %i' % (size, 2 **(8*size)))
yield from ((word & 0xFF << (8*i)) >> (8 * i) for i in range(size))

def pack_message3(DeviceID, Offset, dataLength=None,
Byte0=None, Byte1=None, Byte2=None, Byte3=None,
Word0=None, Word1=None, DWord0=None):

Offset_low, Offset_high = parse_offset(Offset)

if dataLength is not None and not 1 <= dataLength <= 4:
raise ValueError('dataLength should be between 1 and 4')

data = (
(4, (DWord0,)),
(2, (Word0, Word1)),
(1, (Byte0, Byte1, Byte2, Byte3)),
)

for size, words in data:
result = tuple(generalized_parse_word(size, words))
if result != ():
break

if dataLength is None:
dataLength = len(result)

result = pad_tuple_to(result, 4, 0) # if result should be padded with 0's'
# print(result, dataLength)
return bytearray([DeviceID, dataLength, Offset_low, Offset_high, *result])

аргументы

Вместо того, чтобы назвать вручную на разных байтов или слов, я бы принял в коллекцию в качестве аргумента, что-то вроде этого:

def pack_message4(DeviceID, Offset, dataLength=None,
dwords=None, words=None, bytes_=None):
...
data = ((4, dwords), (2, words), (1, bytes))
...

тестирование

inputs = [
{'Word0': 0x1122, 'Word1': 0x3344},
{'DWord0': 0x11223344},
{'Byte0':34, 'Byte1':17, 'Byte2':68, 'Byte3': 51},
]
for i in inputs:
ans0 = packMessage(DeviceID=3, Offset=220, **i)
ans1 = pack_message3(DeviceID=3, Offset=220, **i)
print(ans0 == ans1, ans0, ans1)


True bytearray(b'\x03\x04\xdc\x00"\x11D3') bytearray(b'\x03\x04\xdc\x00"\x11D3')
True bytearray(b'\x03\x04\xdc\x00D3"\x11') bytearray(b'\x03\x04\xdc\x00D3"\x11')
False bytearray(b'\x03\x03\xdc\x00"\x11D3') bytearray(b'\x03\x04\xdc\x00"\x11D3')

Это может быть ваш метод расчета dataLength в случае байт по 1

1
ответ дан 21 марта 2018 в 04:03 Источник Поделиться