2012-08-09 32 views
23

¿Cuál es la diferencia entre array.array('B') y 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 

¿Hay alguna diferencia de memoria o velocidad? ¿Cuál es el caso de uso preferido de cada uno?

Respuesta

12

bytearray es el sucesor del tipo string de Python 2.x. Básicamente es el tipo de matriz de bytes incorporada. A diferencia del tipo original string, es mutable.

El módulo array, por otro lado, se creó para crear estructuras de datos binarios para comunicarse con el mundo exterior (por ejemplo, para leer/escribir formatos de archivo binarios).

A diferencia de bytearray, admite todo tipo de elementos de matriz. Es flexible

Así que si solo necesitas una matriz de bytes, bytearray debería funcionar bien. Si necesita formatos flexibles (por ejemplo, cuando el tipo de elemento de la matriz debe determinarse en tiempo de ejecución), array.array es su amigo.

Sin mirar el código, supongo que bytearray es probablemente más rápido ya que no tiene que considerar diferentes tipos de elementos. Pero es posible que array('B') devuelva bytearray.

+10

De hecho, me gustaría decir 'bytes' es el sucesor de' str' de Python 2.x, no 'bytearray'. –

+0

Eso tampoco es tan exacto. El 'str' de Python 2.x es como una mezcla de str y bytes de Python 3.x. – Broseph

+1

@Broseph: Bueno, 'bytes' * es * un sucesor de' str' de 2.x que se dirige a la "secuencia de bytes indiferenciada" caso de uso ... – SamB

6

bytearray tiene todos los métodos habituales de str. Puedes verlo como un str mutable (bytes en Python3)

Mientras que array.array está orientado a leer y escribir archivos. 'B' es sólo un caso especial para array.array

Usted puede ver que hay una gran diferencia mirando el dir() de cada

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

Aparte de lo que todo el mundo dice, mirando a la fuente parece la forma en que se almacenan es bastante similar (ambas son solo matrices) por lo que no debería haber una gran diferencia. 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; 

y 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

Eso no es una matriz, es la 'lista' incorporada, busque 'arraymodule.c'. – jamylak

+0

ah sí, buena captura. no se pudo encontrar una matriz en ninguna parte de las inclusiones y alguien publicó el 'PyListObject' como la estructura para' array' aquí antes. Fijo. – SamYonnou

+1

Lamento ser franco, pero esta respuesta no explica * nada en absoluto *. ¿Por qué diantre ha sido aceptado? –

-1

Casi nunca se necesita usar array.array módulo usted mismo. Generalmente se usa para crear datos binarios para el formato de archivo binario o protocolo, como el módulo struct.

bytearray se utiliza generalmente para tratar con texto codificado (por ejemplo, UTF-8, ascii, etc), en oposición a Python 3 de str() o de unicode() Python 2 que se utiliza para el texto Unicode.

La mayoría de las veces, debe utilizar str() cuando se trata de texto, o lista y tupla cuando necesita una colección de elementos, incluidos los números.

+3

Lo siento, es un @Lie. array.array siempre es útil cuando necesita una representación eficiente de la memoria de un grupo de datos. – RayLuo

+1

@Iceberg: sí, pero la mayoría de las veces usarías tupla o lista, o algunas veces numpy. En raras ocasiones es necesario o una buena idea usar 'array.array()'. –

+0

'bytes' se usa para tratar con texto codificado, similar al tipo str de Python 2. 'bytearray' es una versión mutable de' bytes'. – Antimony

3

Python Patterns - An Optimization Anecdote es una buena lectura que apunta a array.array('B') como rápido.Uso de la función timing() de este ensayo demuestran que array.array('B') es más rápido que 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 medida timeit realidad muestra array.array('B') veces es más del doble de la velocidad de bytearray()

estaba interesado específicamente en la forma más rápida para empacar una dirección IP en una cadena de cuatro bytes para clasificar. Parece que ni str(bytearray(addr)) ni array('B', addr).tostring() se acercan a la velocidad de pack('4B', *addr).

+0

Creo que empaquetar una dirección IP en int sería en realidad aún más rápido para la clasificación. –

+0

@LieRyan: ¿No tendrían que enfrentar problemas de signo o implicar aritmética de precisión múltiple? – SamB

1

Desde mi prueba, ambos utilizados amostly mismo tamaño de la memoria pero la velocidad de bytearry es 1,5 veces de la matriz cuando creo un gran buffer a leer y escribir.

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

¿Podría darnos un poco más de detalles sobre sus cargas de trabajo de prueba? – SamB

+1

@SamB por cierto, ¿eres un robot? – salmon

+0

No, pero estaba pasando por una de las colas de revisión, probablemente la de "respuestas tardías". De todos modos, el número es mucho más significativo ahora, gracias. – SamB