Подтвердить что ты не робот

Где используются python bytearrays?

Недавно я наткнулся на dataType, называемый bytearray в python. Может ли кто-нибудь предоставить сценарии, в которых требуются bytearrays?

4b9b3361

Ответ 1

A bytearray очень похож на регулярную строку python (str в python2.x, bytes в python3), но с важным отличием, тогда как строки неизменяемы, bytearray являются изменяемыми, немного похожими на a list одиночных символьных строк.

Это полезно, потому что некоторые приложения используют байтовые последовательности способами, которые плохо работают с неизменяемыми строками. Когда вы делаете много небольших изменений в середине больших фрагментов памяти, например, в движке базы данных или библиотеке изображений, строки работают довольно плохо; так как вам нужно сделать копию целой (возможно большой) строки. bytearray имеют то преимущество, что позволяют сделать такое изменение, не сделав сначала копию памяти.

Но этот конкретный случай на самом деле скорее является исключением, а не правилом. В большинстве случаев используется сравнение строк или форматирование строк. Для последнего обычно есть копия, поэтому переменный тип не будет иметь никакого преимущества, а для первого, поскольку неизменяемые строки не могут измениться, вы можете вычислить строку hash строки и сравнить ее как ярлык для сравнения каждого байта в порядке, что почти всегда является большой победой; и поэтому это неизменный тип (str или bytes), который является значением по умолчанию; и bytearray является исключением, когда вам нужны специальные функции.

Ответ 2

Этот ответ был бесстыдным, сорванным с здесь

Пример 1: Сборка сообщения из фрагментов

Предположим, что вы пишете сетевой код, который получает большое сообщение в сокетном соединении. Если вы знаете о сокетах, вы знаете, что операция recv() не дожидается, когда все данные будут отправлены. Вместо этого он просто возвращает то, что доступно в системных буферах. Поэтому, чтобы получить все данные, вы можете написать код, который выглядит следующим образом:

# remaining = number of bytes being received (determined already)
msg = b""
while remaining > 0:
    chunk = s.recv(remaining)    # Get available data
    msg += chunk                 # Add it to the message
    remaining -= len(chunk)  

Единственная проблема с этим кодом заключается в том, что конкатенация (+=) имеет ужасную производительность. Поэтому общая оптимизация производительности в Python 2 состоит в том, чтобы собрать все куски в списке и выполнить соединение, когда вы закончите. Вот так:

# remaining = number of bytes being received (determined already)
msgparts = []
while remaining > 0:
    chunk = s.recv(remaining)    # Get available data
    msgparts.append(chunk)       # Add it to list of chunks
    remaining -= len(chunk)  
msg = b"".join(msgparts)          # Make the final message

Теперь вот третье решение, использующее bytearray:

# remaining = number of bytes being received (determined already)
msg = bytearray()
while remaining > 0:
    chunk = s.recv(remaining)    # Get available data
    msg.extend(chunk)            # Add to message
    remaining -= len(chunk)  

Обратите внимание, что версия bytearray действительно чистая. Вы не собираете части в списке, и вы не выполняете это критическое соединение в конце. Ницца.

Конечно, большой вопрос заключается в том, выполняет ли он или нет. Чтобы проверить это, я сначала составил список небольших байтовых фрагментов, например:

chunks = [b"x"*16]*512

Затем я использовал модуль timeit для сравнения следующих двух фрагментов кода:

# Version 1
msgparts = []
for chunk in chunks:
    msgparts.append(chunk)
msg = b"".join(msgparts)

#Version 2
msg = bytearray()
for chunk in chunks:
    msg.extend(chunk)

При тестировании версия 1 кода выполнялась в 99,8 с, тогда как версия 2 работала в 116,6 с (при использовании сравнения с += конкатенация занимает 230,3 с). Таким образом, при выполнении операции соединения все еще быстрее, она только быстрее примерно на 16%. Лично я думаю, что более чистое программирование версии bytearray может наверстать упущенное.

Пример 2: упаковка двоичной записи

Этот пример - небольшой поворот в последнем примере. Предположим, у вас был большой список Python целых (x, y) координат. Что-то вроде этого: points = [(1,2),(3,4),(9,10),(23,14),(50,90),...] Теперь предположим, что вам нужно записать эти данные в виде двоичного кодированного файла, состоящего из 32-разрядной целочисленной длины, за которой следует каждая точка, упакованная в пару из 32-битных целых чисел. Один из способов сделать это - использовать структурный модуль следующим образом:

import struct
f = open("points.bin","wb")
f.write(struct.pack("I",len(points)))
for x,y in points:
    f.write(struct.pack("II",x,y))
f.close()

Единственная проблема с этим кодом состоит в том, что он выполняет большое количество небольших операций write(). Альтернативный подход состоит в том, чтобы упаковать все в bytearray и выполнять только одну запись в конце. Например:

import struct
f = open("points.bin","wb")
msg = bytearray()
msg.extend(struct.pack("I",len(points))
for x,y in points:
    msg.extend(struct.pack("II",x,y))
f.write(msg)
f.close()

Конечно, версия, использующая bytearray, работает намного быстрее. В простом тесте времени, включающем список из 100000 пунктов, он работает примерно в половине случаев, как версия, которая делает много небольших записей.

Пример 3: Математическая обработка байтовых значений

Тот факт, что bytearrays представляют собой массивы целых чисел, облегчает выполнение определенных видов вычислений. В недавнем проекте встроенных систем я использовал Python для связи с устройством через последовательный порт. В рамках протокола связи все сообщения должны были быть подписаны с байтом проверки продольной избыточности (LRC). LRC вычисляется путем вычисления XOR по всем байтовым значениям. Быстрое вычисление облегчает такие вычисления. Здесь одна версия:

message = bytearray(...)     # Message already created
lrc = 0
for b in message:
    lrc ^= b
message.append(lrc)          # Add to the end of the message

Здесь версия, повышающая безопасность вашей работы: message.append(functools.reduce(lambda x,y:x^y,message)) И вот тот же расчет в Python 2 без bytearray s:

message = "..."       # Message already created
lrc = 0
for b in message:
    lrc ^= ord(b)
message += chr(lrc)        # Add the LRC byte

Лично мне нравится версия bytearray. Нет необходимости использовать ord(), и вы можете просто добавить результат в конце сообщения, а не использовать конкатенацию.

Вот еще один симпатичный пример. Предположим, вы хотели запустить bytearray через простой XOR-шифр. Для этого нужно сделать один слой:

>>> key = 37
>>> message = bytearray(b"Hello World")
>>> s = bytearray(x ^ key for x in message)
>>> s
bytearray(b'[email protected]\x05rJWIA')
>>> bytearray(x ^ key for x in s)
bytearray(b"Hello World")
>>> 

Здесь - ссылка на презентацию

Ответ 3

Если вы посмотрите на документацию для bytearray, в ней говорится:

Возвращает новый массив байтов. Тип bytearray является изменяемой последовательностью целых чисел в диапазоне 0 <= x < 256.

Напротив, в документации для bytes говорится:

Возвращает новый "байтовый" объект, который является неизменной последовательностью целых чисел в диапазоне 0 <= x < 256. bytes - неизменяемая версия bytearray - она ​​имеет те же самые методы, что и без мутаций, и те же операции индексирования и нарезки.

Как вы можете видеть, основное различие - это изменчивость. str методы, которые "меняют" строку, фактически возвращают новую строку с желаемой модификацией. В то время как методы bytearray, которые изменяют последовательность, фактически изменяют последовательность.

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

Ответ 4

В Википедии приведен пример XOR-шифрования с использованием Python bytearrays (docstrings reduced):

#!/usr/bin/python2.7

from os import urandom

def vernam_genkey(length):
    """Generating a key"""
    return bytearray(urandom(length))

def vernam_encrypt(plaintext, key):
    """Encrypting the message."""
    return bytearray([ord(plaintext[i]) ^ key[i] for i in xrange(len(plaintext))])

def vernam_decrypt(ciphertext, key):
    """Decrypting the message"""
    return bytearray([ciphertext[i] ^ key[i] for i in xrange(len(ciphertext))])

def main():
    myMessage = """This is a topsecret message..."""
    print 'message:',myMessage
    key = vernam_genkey(len(myMessage))
    print 'key:', str(key)
    cipherText = vernam_encrypt(myMessage, key)
    print 'cipherText:', str(cipherText)
    print 'decrypted:', vernam_decrypt(cipherText,key)

    if vernam_decrypt(vernam_encrypt(myMessage, key),key)==myMessage:
        print ('Unit Test Passed')
    else:
        print('Unit Test Failed - Check Your Python Distribution')

if __name__ == '__main__':
    main()