2016-03-19 4 views
1

У меня есть bytearray с длиной 2*n:эффективный способ поменять местами байты в питона

a1 a2 b1 b2 c1 c2 

мне нужно переключить байт обратный порядок байт в каждом 2-байтовое слово, и сделать:

a2 a1 b2 b1 c2 c1 

сейчас Я использую следующий подход, но это очень медленно для моей задачи:

converted = bytearray([]) 
for i in range(int(len(chunk)/2)): 
    converted += bytearray([ chunk[i*2+1], chunk[i*2] ]) 

Можно ли переключать endia n из bytearray, вызвав некоторую функцию system/libc?


Ок, спасибо всем, я приурочен некоторые предложения:

import timeit 

test = [ 
""" 
converted = bytearray([]) 
for i in range(int(len(chunk)/2)): 
    converted += bytearray([ chunk[i*2+1], chunk[i*2] ]) 
""", 
""" 
for i in range(0, len(chunk), 2): 
    chunk[i], chunk[i+1] = chunk[i+1], chunk[i] 
""", 
""" 
byteswapped = bytearray([0]) * len(chunk) 
byteswapped[0::2] = chunk[1::2] 
byteswapped[1::2] = chunk[0::2] 
""", 
""" 
chunk[0::2], chunk[1::2] = chunk[1::2], chunk[0::2] 
""" 
] 

for t in test: 
    print(timeit.timeit(t, setup='chunk = bytearray([1]*10)')) 

и результат будет:

$ python ti.py 
11.6219761372 
2.61883187294 
3.47194099426 
1.66421198845 

Так-темп присваивания среза с шагом 2 теперь быстро , Также благодаря г-F для подробно объяснить, но я еще не пробовал из-за numpy

ответ

4

Вы можете использовать назначение срезов с шагом 2:

byteswapped = bytearray(len(original)) 
byteswapped[0::2] = original[1::2] 
byteswapped[1::2] = original[0::2] 

Или, если вы хотите сделать это на месте :

original[0::2], original[1::2] = original[1::2], original[0::2] 

синхронизации показывает, что нарезка в широком масштабе превосходит петлю Python уровня для больших массивов:

>>> timeit.timeit(''' 
... for i in range(0, len(chunk), 2): 
...  chunk[i], chunk[i+1] = chunk[i+1], chunk[i]''', 
... 'chunk=bytearray(1000)') 
81.70195105159564 
>>> 
>>> timeit.timeit(''' 
... byteswapped = bytearray(len(original)) 
... byteswapped[0::2] = original[1::2] 
... byteswapped[1::2] = original[0::2]''', 
... 'original=bytearray(1000)') 
2.1136113323948393 
>>> 
>>> timeit.timeit('chunk[0::2], chunk[1::2] = chunk[1::2], chunk[0::2]', 'chunk= 
bytearray(1000)') 
1.79349659994989 

Для небольших массивов, отрезая все еще бьется явный цикл, но разница не так велика:

>>> timeit.timeit(''' 
... for i in range(0, len(chunk), 2): 
...  chunk[i], chunk[i+1] = chunk[i+1], chunk[i]''', 
... 'chunk=bytearray(10)') 
1.2503637694328518 
>>> 
>>> timeit.timeit(''' 
... byteswapped = bytearray(len(original)) 
... byteswapped[0::2] = original[1::2] 
... byteswapped[1::2] = original[0::2]''', 
... 'original=bytearray(10)') 
0.8973060929306484 
>>> 
>>> timeit.timeit('chunk[0::2], chunk[1::2] = chunk[1::2], chunk[0::2]', 'chunk= 
bytearray(10)') 
0.6282232971918802 
+0

Niiiiiiiiiiice. – kindall

+0

выглядит очень круто – user3479125

+0

@ user3479125: Я исправил неэффективность в том, как мой код создавал массив 'byteswapped'; он должен теперь превзойти явный цикл, если вы измените свои тайминги. Я также добавил решение на месте, которое должно быть еще быстрее. Кроме того, если вы попробуете свои тайминги с более крупными массивами, вы обнаружите, что явный цикл масштабируется намного хуже, чем нарезка из-за больших накладных интерпретаторов. – user2357112

2

Если вы используете frombuffer функцию Numpy, вы можете построить NumPy ndarray, что на самом деле разделяет физическую память bytearray, а затем операции по замене могут выполняться не на копии, а на месте.

Вы можете просто выполнить ту же самую индексацию непосредственно на byt, такие как

byt[i] = byt[i+1] 

но получить дескриптор буферного массива с явным типом в NumPy часто позволяет сделать гораздо больше, особенно с bytearray, который очень ограниченный сам по себе.

Будьте осторожны, хотя. Несмотря на то, что на уровне Python bytearray представляет собой неподписанные 8-битные целочисленные значения (0-255), фактическая реализация C-кода в bytearrayobject.h использует значение char для байтовых значений (see here for more info). Если вы используете numpy для этого, вы, вероятно, хотите указать необязательный аргумент dtype для frombuffer, с dtype=numpy.ubyte.

import numpy as np 
byt = bytearray(range(256)) 
npbyt = np.frombuffer(byt, dtype=np.ubyte) 

for i in range(0, len(npbyt)-1, 2): 
    temp = npbyt[i] 
    npbyt[i] = npbyt[i+1] 
    npbyt[i+1] = temp 

print(list(byt)) 

Он печатает

[1, 
0, 
3, 
2, 
5, 
4, 
... 
255, 
254] 

я столкнулся с некоторыми из этих вопросов при работе на небольшой стороне проекта под названием buffersort, который может выполнять в месте сортировки на объекты Python, которые реализуют запись способный протокол буфера , как bytearray ...

Если вы заинтересованы в том, чтобы захватить источник Cython оттуда, есть простая вспомогательная функция _swap, которая упростит вашу работу. Это, вероятно, слишком переполняет ваш случай использования.

1

Вы также можете обменять их на место и использовать исходный массив.

chunk = bytearray([1,2,3,4]) 

for i in range(0, len(chunk), 2): 
    chunk[i], chunk[i+1] = chunk[i+1], chunk[i]