2010-02-24 9 views
5

¿Existe un lenguaje común para evitar la copia rebanada sentido para casos como éste:Evitar la copia rebanada innecesaria en Python

>>> a = bytearray(b'hello') 
>>> b = bytearray(b'goodbye, cruel world.') 
>>> a.extend(b[14:20]) 
>>> a 
bytearray(b'hello world') 

Me parece que hay una copia innecesaria ocurre cuando se crea la rebanada b[14:20]. En lugar de crear un nuevo segmento en la memoria para dar a extend, quiero decir "use solo este rango del objeto actual".

Algunos métodos le ayudará a cabo con los parámetros de división, por ejemplo count:

>>> a = bytearray(1000000)  # a million zero bytes 
>>> a[0:900000].count(b'\x00') # expensive temporary slice 
900000 
>>> a.count(b'\x00', 0, 900000) # helpful start and end parameters 
900000 

pero muchos, como extend en mi primer ejemplo, no tienen esta característica.

Me doy cuenta de que para muchas aplicaciones de lo que estoy hablando sería una micro-optimización, entonces antes de que alguien pregunte, sí, he perfilado mi aplicación, y es algo que vale la pena preocuparse por mi caso.

Tengo una 'solución' a continuación, pero cualquier idea mejor es bienvenida.

Respuesta

5

Creación de un objeto buffer evita la copia de la loncha, pero para las rebanadas cortos es más eficiente que acaba de hacer la copia:

>>> a.extend(buffer(b, 14, 6)) 
>>> a 
bytearray(b'hello world') 

Aquí sólo hay una copia hecha de la memoria, pero el costo de la creación de la buffer el objeto más que borra el ahorro. Sin embargo, debería ser mejor para rebanadas más grandes. No estoy seguro de cuán grande debería ser la porción para que este método sea más eficiente en general.

Tenga en cuenta que para Python 3 (y opcionalmente en Python 2.7) que había necesidad de un objeto en lugar memoryview:

>>> a.extend(memoryview(b)[14:20]) 
+0

El búfer es una buena opción para los objetos que admiten la interfaz del búfer. Por lo general, no vale la carcasa especial para casos pequeños (a menos que la mayoría de los casos de uso sean pequeños) porque un 50% más que una pequeña cantidad es todavía una pequeña cantidad –

2

itertools tiene islice. islice no tiene un método de recuento, por lo que es útil en otros casos en los que desea evitar copiar el sector. Como ha señalado, el conteo tiene un mecanismo para eso de todos modos

>>> from itertools import islice 
>>> a = bytearray(1000000) 
>>> sum(1 for x in islice(a,0,900000) if x==0) 
900000 
>>> len(filter(b'\x00'.__eq__,islice(a,0,900000))) 
900000 

>>> a=bytearray(b"hello") 
>>> b = bytearray(b'goodbye, cruel world.') 
>>> a.extend(islice(b,14,20)) 
>>> a 
bytearray(b'hello world') 
+0

'islice' es una buena alternativa. Acabo de hacer algunas pruebas rápidas y parece tan rápido como 'buffer' cuando se usa con' extender', sin embargo, son * mucho * más lentas que el simple uso de una rebanada, incluso para medio millón de elementos ... –

Cuestiones relacionadas