Hola usuario de nuestro sitio, descubrimos la respuesta a lo que andabas buscando, has scroll y la hallarás aquí.
Solución:
Si le preocupa el rendimiento de este código y un int
como un byte no es una interfaz adecuada en su caso, entonces probablemente debería reconsiderar las estructuras de datos que usa, por ejemplo, use str
objetos en su lugar.
Podrías cortar el bytes
objeto para obtener 1 longitud bytes
objetos:
L = [bytes_obj[i:i+1] for i in range(len(bytes_obj))]
Hay PEP 0467: mejoras menores de API para secuencias binarias que propone bytes.iterbytes()
método:
>>> list(b'123'.iterbytes())
[b'1', b'2', b'3']
int.to_bytes
int
los objetos tienen un método to_bytes que se puede usar para convertir un int en su byte correspondiente:
>>> import sys
>>> [i.to_bytes(1, sys.byteorder) for i in b'123']
[b'1', b'2', b'3']
Al igual que con otras respuestas, no está claro que esto sea más legible que la solución original del OP: creo que los argumentos de longitud y orden de bytes lo hacen más ruidoso.
estructura.desempaquetar
Otro enfoque sería usar struct.unpack, aunque esto también podría considerarse difícil de leer, a menos que esté familiarizado con el módulo struct:
>>> import struct
>>> struct.unpack('3c', b'123')
(b'1', b'2', b'3')
(Como jfs observa en los comentarios, el formato string por struct.unpack
se puede construir dinámicamente; en este caso, sabemos que la cantidad de bytes individuales en el resultado debe ser igual a la cantidad de bytes en la cadena de bytes original, por lo que struct.unpack(str(len(bytestring)) + 'c', bytestring)
es posible.)
Rendimiento
>>> import random, timeit
>>> bs = bytes(random.randint(0, 255) for i in range(100))
>>> # OP's solution
>>> timeit.timeit(setup="from __main__ import bs",
stmt="[bytes([b]) for b in bs]")
46.49886950897053
>>> # Accepted answer from jfs
>>> timeit.timeit(setup="from __main__ import bs",
stmt="[bs[i:i+1] for i in range(len(bs))]")
20.91463226894848
>>> # Leon's answer
>>> timeit.timeit(setup="from __main__ import bs",
stmt="list(map(bytes, zip(bs)))")
27.476876026019454
>>> # guettli's answer
>>> timeit.timeit(setup="from __main__ import iter_bytes, bs",
stmt="list(iter_bytes(bs))")
24.107485140906647
>>> # user38's answer (with Leon's suggested fix)
>>> timeit.timeit(setup="from __main__ import bs",
stmt="[chr(i).encode('latin-1') for i in bs]")
45.937552741961554
>>> # Using int.to_bytes
>>> timeit.timeit(setup="from __main__ import bs;from sys import byteorder",
stmt="[x.to_bytes(1, byteorder) for x in bs]")
32.197659170022234
>>> # Using struct.unpack, converting the resulting tuple to list
>>> # to be fair to other methods
>>> timeit.timeit(setup="from __main__ import bs;from struct import unpack",
stmt="list(unpack('100c', bs))")
1.902243083808571
struct.unpack
parece ser al menos un orden de magnitud más rápido que otros métodos, presumiblemente porque opera a nivel de byte. int.to_bytes
por otro lado, funciona peor que la mayoría de los enfoques “obvios”.
Pensé que podría ser útil comparar los tiempos de ejecución de los diferentes enfoques, así que hice un punto de referencia (usando mi biblioteca simple_benchmark
):
Probablemente, como era de esperar, la solución NumPy es, con mucho, la solución más rápida para objetos de bytes grandes.
Pero si se desea una lista resultante, entonces tanto la solución NumPy (con el tolist()
) y el struct
solución son mucho más rápidos que las otras alternativas.
No incluí la respuesta de guettlis porque es casi idéntica a la solución jfs, solo que en lugar de una comprensión, se usa una función de generador.
import numpy as np
import struct
import sys
from simple_benchmark import BenchmarkBuilder
b = BenchmarkBuilder()
@b.add_function()
def jfs(bytes_obj):
return [bytes_obj[i:i+1] for i in range(len(bytes_obj))]
@b.add_function()
def snakecharmerb_tobytes(bytes_obj):
return [i.to_bytes(1, sys.byteorder) for i in bytes_obj]
@b.add_function()
def snakecharmerb_struct(bytes_obj):
return struct.unpack(str(len(bytes_obj)) + 'c', bytes_obj)
@b.add_function()
def Leon(bytes_obj):
return list(map(bytes, zip(bytes_obj)))
@b.add_function()
def rusu_ro1_format(bytes_obj):
return [b'%c' % i for i in bytes_obj]
@b.add_function()
def rusu_ro1_numpy(bytes_obj):
return np.frombuffer(bytes_obj, dtype='S1')
@b.add_function()
def rusu_ro1_numpy_tolist(bytes_obj):
return np.frombuffer(bytes_obj, dtype='S1').tolist()
@b.add_function()
def User38(bytes_obj):
return [chr(i).encode() for i in bytes_obj]
@b.add_arguments('byte object length')
def argument_provider():
for exp in range(2, 18):
size = 2**exp
yield size, b'a' * size
r = b.run()
r.plot()
Recuerda comunicar este artículo si si solucionó tu problema.