在Python 3中迭代bytes
对象时,将bytes
个人ints
作为>>> [b for b in b'123']
[49, 50, 51]
:
bytes
如何获得1长>>> [bytes([b]) for b in b'123']
[b'1', b'2', b'3']
个对象?
以下是可能的,但对读者来说不是很明显,而且很可能表现不好:
{{1}}
答案 0 :(得分:23)
如果您担心此代码的性能并且int
因为字节不适合您的情况,那么您应该重新考虑您使用的数据结构,例如,使用str
对象。
您可以对bytes
对象进行切片以获得1个bytes
个对象:
L = [bytes_obj[i:i+1] for i in range(len(bytes_obj))]
有PEP 0467 -- Minor API improvements for binary sequences提出bytes.iterbytes()
方法:
>>> list(b'123'.iterbytes())
[b'1', b'2', b'3']
答案 1 :(得分:13)
int.to_bytes
int
对象具有一种to_bytes方法,可用于将int转换为其相应的字节:
>>> import sys
>>> [i.to_bytes(1, sys.byteorder) for i in b'123']
[b'1', b'2', b'3']
与其他一些答案一样,尚不清楚这比OP的原始解决方案更具可读性:length和byteorder参数使它听起来更嘈杂。
struct.unpack
另一种方法是使用struct.unpack,尽管除非您熟悉struct模块,否则这也可能很难阅读:
>>> import struct
>>> struct.unpack('3c', b'123')
(b'1', b'2', b'3')
(如jfs在注释中观察到的,struct.unpack
的格式字符串可以动态构造;在这种情况下,我们知道结果中的单个字节数必须等于原始字节串中的字节数,因此struct.unpack(str(len(bytestring)) + 'c', bytestring)
是可能的。)
性能
>>> 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
似乎比其他方法至少快一个数量级,大概是因为它在字节级别运行。另一方面,int.to_bytes
的效果比大多数“显而易见”的方法都要差。
答案 2 :(得分:6)
自python 3.5开始,您可以使用% formatting to bytes and bytearray:
[b'%c' % i for i in b'123']
输出:
[b'1', b'2', b'3']
上述解决方案比您的初始方法快2-3倍,如果您想要更快的解决方案,我建议您使用numpy.frombuffer:
import numpy as np
np.frombuffer(b'123', dtype='S1')
输出:
array([b'1', b'2', b'3'],
dtype='|S1')
第二种解决方案比struct.unpack快10%(我使用了与@snakecharmerb相同的性能测试,针对100个随机字节)
答案 3 :(得分:6)
我认为比较不同方法的运行时可能有用,因此我做了一个基准测试(使用我的库simple_benchmark
):
毫无疑问,NumPy解决方案是迄今为止大字节对象最快的解决方案。
但是,如果需要结果列表,则NumPy解决方案(带有tolist()
)和struct
解决方案都比其他方法快得多。
我没有包括guettlis答案,因为它几乎与jfs解决方案相同,只是不理解使用了生成器函数。
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()
答案 4 :(得分:5)
由map()
,bytes()
和zip()
组成的三重奏可以达到目的:
>>> list(map(bytes, zip(b'123')))
[b'1', b'2', b'3']
但是我不认为它比[bytes([b]) for b in b'123']
更具可读性或表现更好。
答案 5 :(得分:4)
我使用此辅助方法:
def iter_bytes(my_bytes):
for i in range(len(my_bytes)):
yield my_bytes[i:i+1]
适用于Python2和Python3。
答案 6 :(得分:0)
一种简短的方法:
[chr(i).encode() for i in b'123']
答案 7 :(得分:-1)
我想知道数组对象是否更适合您的目的并避免不必要的转换。