Domanda Dove vengono utilizzati i pitone di pitarray?


Recentemente mi sono imbattuto nel dataType chiamato bytearray in python. Qualcuno potrebbe fornire scenari in cui sono richiesti i beati?


44
2018-02-01 16:09


origine


risposte:


UN bytearray è molto simile a una normale stringa di python (str in python2.x, bytes in python3) ma con un'importante differenza, mentre le stringhe lo sono immutabile, bytearrays sono mutevoli, un po 'come a list di stringhe di carattere singolo.

Questo è utile perché alcune applicazioni usano sequenze di byte in modi che funzionano male con stringhe immutabili. Quando si apportano molte piccole modifiche nel mezzo di grandi blocchi di memoria, come in un motore di database o in una libreria di immagini, le stringhe si comportano piuttosto male; dal momento che devi fare una copia dell'intera (possibilmente grande) stringa. bytearrays hanno il vantaggio di rendere possibile fare quel tipo di cambiamento senza prima fare una copia della memoria.

Ma questo caso particolare è in realtà più l'eccezione, piuttosto che la regola. La maggior parte degli usi comporta il confronto di stringhe o la formattazione di stringhe. Per quest'ultimo, di solito c'è una copia, quindi un tipo mutabile non offrirebbe alcun vantaggio, e per il primo, poiché le stringhe non modificabili non possono cambiare, è possibile calcolare un hash della stringa e confrontarla come una scorciatoia per confrontare ogni byte in ordine, che è quasi sempre una grande vittoria; e quindi è il tipo immutabile (str o bytes) questo è il valore predefinito; e bytearray è l'eccezione quando hai bisogno delle sue caratteristiche speciali.


50
2018-02-01 16:22



Questa risposta è stata svergognata strappata via Qui 

Esempio 1: assemblaggio di un messaggio da frammenti

Supponiamo che tu stia scrivendo del codice di rete che sta ricevendo un messaggio di grandi dimensioni su una connessione socket. Se conosci le prese, sai che il recv() operazione non aspetta che tutti i dati arrivino. Invece, restituisce semplicemente ciò che è attualmente disponibile nei buffer di sistema. Pertanto, per ottenere tutti i dati, potresti scrivere un codice simile al seguente:

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

L'unico problema con questo codice è quella concatenazione (+=) ha prestazioni orribili. Pertanto, una ottimizzazione delle prestazioni comune in Python 2 è quella di raccogliere tutti i blocchi in un elenco ed eseguire un join quando hai finito. Come questo:

# 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

Ora, ecco una terza soluzione che utilizza a 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)  

Notare come il bytearray la versione è veramente pulita. Non raccogli le parti in una lista e non esegui quel criptico join alla fine. Bello.

Certo, la grande domanda è se si esibisce o no. Per verificarlo, ho prima creato una lista di piccoli frammenti di byte come questo:

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

Ho quindi utilizzato il modulo timeit per confrontare i seguenti due frammenti di codice:

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

Quando testato, la versione 1 del codice funzionava a 99.8s mentre la versione 2 funzionava a 116.6s (una versione che utilizzava += la concatenazione richiede 230.3 al confronto). Quindi, mentre eseguire un'operazione di join è ancora più veloce, è solo più veloce di circa il 16%. Personalmente, penso che la programmazione più pulita del bytearray la versione potrebbe compensarlo.

Esempio 2: imballaggio di record binari

Questo esempio è una leggera svolta sull'ultimo esempio. Supponiamo che tu abbia una grande lista di Python di coordinate integer (x, y). Qualcosa come questo: points = [(1,2),(3,4),(9,10),(23,14),(50,90),...] Ora, supponiamo di dover scrivere quei dati come un file codificato in binario che consiste in una lunghezza intera di 32 bit seguita da ciascun punto inserito in una coppia di numeri interi a 32 bit. Un modo per farlo sarebbe utilizzare il modulo struct in questo modo:

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

L'unico problema con questo codice è che esegue un numero elevato di piccole dimensioni write() operazioni. Un approccio alternativo è quello di mettere tutto in un pacchetto bytearray e solo eseguire una scrittura alla fine. Per esempio:

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

Abbastanza sicuro, la versione che utilizza bytearray corre molto più veloce. In un semplice test di temporizzazione che coinvolge un elenco di 100000 punti, viene eseguito in circa la metà del tempo della versione che esegue molte piccole scritture.

Esempio 3: elaborazione matematica dei valori di byte

Il fatto che i grafici si presentino come array di numeri interi facilita l'esecuzione di determinati tipi di calcoli. In un recente progetto di sistemi embedded, stavo usando Python per comunicare con un dispositivo su una porta seriale. Come parte del protocollo di comunicazione, tutti i messaggi dovevano essere firmati con un byte LRC (Longitudinal Redundancy Check). Un LRC viene calcolato prendendo un XOR su tutti i valori di byte. Bytearrays rende questi calcoli facili. Ecco una versione:

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

Ecco una versione che aumenta la sicurezza del tuo lavoro: message.append(functools.reduce(lambda x,y:x^y,message)) Ed ecco lo stesso calcolo in Python 2 senza bytearrayS:

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

Personalmente, mi piace il bytearray versione. Non c'è bisogno di usare ord() e puoi semplicemente aggiungere il risultato alla fine del messaggio invece di usare la concatenazione.

Ecco un altro esempio carino. Supponi di voler eseguire un bytearray attraverso un semplice codice XOR. Ecco un one-liner per farlo:

>>> key = 37
>>> message = bytearray(b"Hello World")
>>> s = bytearray(x ^ key for x in message)
>>> s
bytearray(b'm@IIJ\x05rJWIA')
>>> bytearray(x ^ key for x in s)
bytearray(b"Hello World")
>>> 

Qui è un link alla presentazione


45
2018-02-01 16:50



Se si guarda la documentazione per bytearray, dice:

Restituisce una nuova matrice di byte. Il tipo pertearray è una sequenza mutabile di numeri interi nell'intervallo 0 <= x <256.

Al contrario, la documentazione per bytes dice:

Restituisce un nuovo oggetto "byte", che è una sequenza immutabile di numeri interi nell'intervallo 0 <= x <256. byte è una versione immutabile di bytearray - ha gli stessi metodi non mutanti e gli stessi comportamenti di indicizzazione e suddivisione.

Come puoi vedere, la distinzione principale è la mutevolezza. str metodi che "cambiano" la stringa restituiscono effettivamente una nuova stringa con la modifica desiderata. Mentre bytearray metodi che cambiano la sequenza in realtà cambia la sequenza.

Preferiresti usare bytearray, se stai modificando un oggetto di grandi dimensioni (ad esempio il buffer di pixel di un'immagine) attraverso la sua rappresentazione binaria e vuoi che le modifiche vengano eseguite sul posto per l'efficienza.


5
2018-02-01 16:27



Wikipedia fornisce un esempio di Codice XOR usando i byTearray di Python (docstrings ridotte):

#!/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()

3
2018-06-14 12:56