在Python中,如何有效地从一个巨大的块中提取具有偏移量的字节?
假设我有这样一个字节块:在Python中,如何有效地从一个巨大的块中提取具有偏移量的字节?,python,Python,假设我有这样一个字节块: block = b'0123456789AB' 我想从4个字节的每个块中提取3个字节的每个序列,并将它们连接在一起。上述区块的结果应为: b'01245689A' # 3, 7 and B are missed 我可以用这样的脚本解决这个问题: block = b'0123456789AB' result = b'' for i in range(0, len(block), 4): result += block[i:i + 3] print(resul
block = b'0123456789AB'
我想从4个字节的每个块中提取3个字节的每个序列,并将它们连接在一起。上述区块的结果应为:
b'01245689A' # 3, 7 and B are missed
我可以用这样的脚本解决这个问题:
block = b'0123456789AB'
result = b''
for i in range(0, len(block), 4):
result += block[i:i + 3]
print(result)
但是众所周知,Python使用for循环和字节连接效率很低,因此如果我将其应用于一个非常大的字节块,我的方法将永远不会结束。那么,有没有更快的执行方法?使其可变并删除不需要的片段
>>> tmp = bytearray(block)
>>> del tmp[3::4]
>>> bytes(tmp)
b'01245689A'
如果你的块很大,并且你想删除几乎所有的字节,那么收集你想要的可能会更快,就像你的一样。虽然您的应用可能需要二次时间,但最好使用join
:
>>> b''.join([block[i : i+3] for i in range(0, len(block), 4)])
b'01245689A'
(顺便说一句,根据它应该是块[i:i+3]
,而不是块[i:i+3]
,并且有充分的理由。)
虽然这会生成很多对象,但这可能是内存问题。对于您所陈述的情况,它比您的要快得多,但比我的bytearray
one慢得多
使用block=b'0123456789AB'*100_000
进行基准测试(比您在下面的评论中提到的1GB小得多):
基准代码:
import timeit
def baseline(block):
pass
def original(block):
result = b''
for i in range(0, len(block), 4):
result += block[i:i + 3]
return result
def Kelly_Bundy_bytearray(block):
tmp = bytearray(block)
del tmp[3::4]
return bytes(tmp)
def Kelly_Bundy_join(block):
return b''.join([block[i : i+3] for i in range(0, len(block), 4)])
funcs = [
baseline,
original,
Kelly_Bundy_bytearray,
Kelly_Bundy_join,
]
block = b'0123456789AB' * 100_000
args = block,
number = 10**0
expect = original(*args)
for func in funcs:
print(func(*args) == expect, func.__name__)
print()
tss = [[] for _ in funcs]
for _ in range(3):
for func, ts in zip(funcs, tss):
t = min(timeit.repeat(lambda: func(*args), number=number)) / number
ts.append(t)
print(*('%8.2f ms ' % (1e3 * t) for t in ts), func.__name__)
print()
“众所周知”——这真的是你的问题吗?如果是的话,如果性能如此重要的话,也许你不应该用python来做这件事。这可能是你想要的吗?@lucidbrot你会如何使用memoryview来完成这件事?哇!令人印象深刻。这很有效。我在1Gb大小的块上尝试了你的代码,它在我的机器上处理了1秒。非常感谢。如果我在每个块中遗漏了超过1个字节,我应该多次应用
del
?@Fomalhaut-Yes,del
。除非你有大数据块并且想删除几乎所有的字节,否则其他的可能会更快。@Fomalhaut在答案中添加了更多的内容。谢谢你的研究!在行b''中加入([block[i:i+3]表示范围内的i(0,len(block),4)])
我建议使用生成器而不是列表,因为内存消耗和性能:b''。加入(block[i:i+3]表示范围内的i(0,len(block,4))
。
import timeit
def baseline(block):
pass
def original(block):
result = b''
for i in range(0, len(block), 4):
result += block[i:i + 3]
return result
def Kelly_Bundy_bytearray(block):
tmp = bytearray(block)
del tmp[3::4]
return bytes(tmp)
def Kelly_Bundy_join(block):
return b''.join([block[i : i+3] for i in range(0, len(block), 4)])
funcs = [
baseline,
original,
Kelly_Bundy_bytearray,
Kelly_Bundy_join,
]
block = b'0123456789AB' * 100_000
args = block,
number = 10**0
expect = original(*args)
for func in funcs:
print(func(*args) == expect, func.__name__)
print()
tss = [[] for _ in funcs]
for _ in range(3):
for func, ts in zip(funcs, tss):
t = min(timeit.repeat(lambda: func(*args), number=number)) / number
ts.append(t)
print(*('%8.2f ms ' % (1e3 * t) for t in ts), func.__name__)
print()