2012-08-09 11 views
23

Qual è la differenza tra array.array('B') e bytearray?Python: bytearray vs array

from array import array 

a = array('B', 'abc') 
b = bytearray('abc') 

a[0] = 100 
b[0] = 'd' 

print a 
print b 

Sono presenti differenze di memoria o di velocità? Qual è il caso d'uso preferito di ciascuno?

risposta

12

bytearray è il successore del tipo string di Python 2.x. È fondamentalmente il tipo di array di byte integrato. A differenza del tipo originale string, è mutabile.

Il modulo array, d'altra parte, è stato creato per creare strutture di dati binari per comunicare con il mondo esterno (ad esempio, per leggere/scrivere i formati di file binari).

A differenza di bytearray, supporta tutti i tipi di elementi di array. È flessibile

Quindi se avete solo bisogno di una matrice di byte, bytearray dovrebbe funzionare bene. Se hai bisogno di formati flessibili (diciamo quando il tipo di elemento dell'array deve essere determinato in fase di runtime), array.array è tuo amico.

Senza guardare il codice, la mia ipotesi sarebbe che bytearray è probabilmente più veloce poiché non deve considerare diversi tipi di elementi. Ma è possibile che array('B') restituisca un bytearray.

+10

avrei davvero dire 'bytes' è il successore di' str' di Python 2.x, non 'bytearray'. –

+0

Non è nemmeno così preciso. Python 2.x's 'str' è come un mix di str e byte di Python 3.x. – Broseph

+1

@Broseph: beh, 'bytes' * è * un successore di 2.x's' str' che prende di mira la "sequenza di byte indifferenziata" use case ... – SamB

6

bytearray ha tutti i soliti metodi str. Puoi farlo come un mutabile str (byte in Python3)

Mentre array.array è orientato alla lettura e scrittura di file. 'B' è solo un caso speciale per array.array

Si può vedere c'è una bella differenza guardando il dir() di ogni

>>> dir(bytearray) 
['__add__', '__alloc__', '__class__', '__contains__', '__delattr__', 
'__delitem__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
'__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', 
'__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', 
'__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 
'capitalize', 'center', 'count', 'decode', 'endswith', 'expandtabs', 'extend', 
'find', 'fromhex', 'index', 'insert', 'isalnum', 'isalpha', 'isdigit', 'islower', 
'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 
'partition', 'pop', 'remove', 'replace', 'reverse', 'rfind', 'rindex', 'rjust', 
'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 
'swapcase', 'title', 'translate', 'upper', 'zfill'] 
>>> dir(array) 
['__add__', '__class__', '__contains__', '__copy__', '__deepcopy__', 
'__delattr__', '__delitem__', '__doc__', '__eq__', '__format__', '__ge__', 
'__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', 
'__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', 
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', 
'__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 
'buffer_info', 'byteswap', 'count', 'extend', 'frombytes', 'fromfile', 
'fromlist', 'fromstring', 'fromunicode', 'index', 'insert', 'itemsize', 'pop', 
'remove', 'reverse', 'tobytes', 'tofile', 'tolist', 'tostring', 'tounicode', 
'typecode'] 
1

parte quanto tutti gli altri, ha detto, guardando il sorgente sembra il modo in cui sono memorizzati è abbastanza simile (entrambi sono solo array), quindi non dovrebbe esserci un'enorme differenza. bytearray:

typedef struct { 
    PyObject_VAR_HEAD 
    /* XXX(nnorwitz): should ob_exports be Py_ssize_t? */ 
    int ob_exports; /* how many buffer exports */ 
    Py_ssize_t ob_alloc; /* How many bytes allocated */ 
    char *ob_bytes; 
} PyByteArrayObject; 

e array:

typedef struct arrayobject { 
    PyObject_VAR_HEAD 
    char *ob_item; 
    Py_ssize_t allocated; 
    struct arraydescr *ob_descr; 
    PyObject *weakreflist; /* List of weak references */ 
    int ob_exports; /* Number of exported buffers */ 
} arrayobject; 
+2

Questo non è array, è la 'lista' incorporata, cerca 'arraymodule.c'. – jamylak

+0

ah sì buona cattura. non è stato possibile trovare l'array in nessuna parte degli include e qualcuno ha pubblicato il 'PyListObject' come struct per' array' qui prima. Fisso. – SamYonnou

+1

Mi dispiace essere schietto, ma questa risposta non spiega * assolutamente nulla *. Perché mai è stato accettato? –

-1

È quasi mai bisogno di usare array.array modulo da soli. Di solito viene utilizzato per la creazione di dati binari per il formato o il protocollo di file binari, come il modulo struct.

bytearray di solito è usato per trattare il testo codificato (ad esempio utf-8, ASCII, ecc), al contrario di Python 3 del str() o Python 2 di unicode() che viene utilizzato per il testo Unicode.

Nella maggior parte dei casi, è necessario utilizzare str() quando si ha a che fare con testo, o lista e tupla quando è necessario un insieme di elementi, compresi i numeri.

+3

Siamo spiacenti, è un @Lie. array.array è sempre utile ogni volta che è necessario rappresentare in modo efficiente la memoria di un gruppo di dati. – RayLuo

+1

@ Iceberg: sì, ma la maggior parte delle volte si usa la tupla o la lista, o talvolta la numpia. Raramente è necessario o una buona idea usare 'array.array()'. –

+0

'byte 'è usato per gestire il testo codificato, simile al tipo str di Python 2. 'bytearray' è una versione mutevole di' bytes'. – Antimony

3

Python Patterns - An Optimization Anecdote è una buona lettura che punta a array.array('B') come veloce.Utilizzando la funzione timing() da quel saggio Mostrano che array.array('B') è più veloce di bytearray():

#!/usr/bin/env python 

from array import array 
from struct import pack 
from timeit import timeit 
from time import clock 

def timing(f, n, a): 
    start = clock() 
    for i in range(n): 
     f(a); f(a); f(a); f(a); f(a); f(a); f(a); f(a); f(a); f(a) 
    finish = clock() 
    return '%s\t%f' % (f.__name__, finish - start) 

def time_array(addr): 
    return array('B', addr) 

def time_bytearray(addr): 
    return bytearray(addr) 

def array_tostring(addr): 
    return array('B', addr).tostring() 

def str_bytearray(addr): 
    return str(bytearray(addr)) 

def struct_pack(addr): 
    return pack('4B', *addr) 

if __name__ == '__main__': 
    count = 10000 
    addr = '192.168.4.2' 
    addr = tuple([int(i) for i in addr.split('.')]) 
    print('\t\ttiming\t\tfunc\t\tno func') 
    print('%s\t%s\t%s' % (timing(time_array, count, addr), 
      timeit('time_array((192,168,4,2))', number=count, setup='from __main__ import time_array'), 
      timeit("array('B', (192,168,4,2))", number=count, setup='from array import array'))) 
    print('%s\t%s\t%s' % (timing(time_bytearray, count, addr), 
      timeit('time_bytearray((192,168,4,2))', number=count, setup='from __main__ import time_bytearray'), 
      timeit('bytearray((192,168,4,2))', number=count))) 
    print('%s\t%s\t%s' % (timing(array_tostring, count, addr), 
      timeit('array_tostring((192,168,4,2))', number=count, setup='from __main__ import array_tostring'), 
      timeit("array('B', (192,168,4,2)).tostring()", number=count, setup='from array import array'))) 
    print('%s\t%s\t%s' % (timing(str_bytearray, count, addr), 
      timeit('str_bytearray((192,168,4,2))', number=count, setup='from __main__ import str_bytearray'), 
      timeit('str(bytearray((192,168,4,2)))', number=count))) 
    print('%s\t%s\t%s' % (timing(struct_pack, count, addr), 
      timeit('struct_pack((192,168,4,2))', number=count, setup='from __main__ import struct_pack'), 
      timeit("pack('4B', *(192,168,4,2))", number=count, setup='from struct import pack'))) 

La misura timeit mostra in realtà array.array('B') a volte è più del doppio della velocità di bytearray()

ero interessato in particolare nel modo più veloce per comprimere un indirizzo IP in una stringa di quattro byte per l'ordinamento. Sembra che né str(bytearray(addr))array('B', addr).tostring() si avvicinino alla velocità di pack('4B', *addr).

+0

Credo che impacchettare un indirizzo IP su int sia in realtà ancora più veloce per l'ordinamento. –

+0

@LieRyan: Non è questo il caso di incorrere in problemi di segno o di aritmetica a precisione multipla? – SamB

1

Dal mio test, entrambi usati amostly stessa dimensione della memoria ma la velocità di bytearry è 1,5 volte di matrice quando si crea un buffer grande di leggere e scrivere.

from array import array 
from time import time 

s = time() 

""" 
map = array('B') 
for i in xrange(256**4/8): 
     map.append(0) 
""" 

#bytearray 
map = bytearray() 
for i in xrange(256**4/8): 
     map.append(0) 
print "init:", time() - s 
+0

Potresti fornire ulteriori dettagli sui carichi di lavoro dei test? – SamB

+1

@SamB btw, sei un robot? – salmon

+0

No, ma stavo esaminando una delle code di revisione, probabilmente quella delle "risposte in ritardo". Ad ogni modo, il numero è sicuramente molto più significativo ora, grazie. – SamB